Skip to content

@Kartikayy007: Add syntax highlighting to code blocks in reports and fix and the Outdated section name #883

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 11, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions report/common.py
Original file line number Diff line number Diff line change
@@ -73,6 +73,7 @@ class Benchmark:
signature: str = ''
project: str = ''
function: str = ''
language: str = ''


@dataclasses.dataclass
@@ -129,6 +130,7 @@ def run_log(self) -> str:
class Target:
code: str
fixer_prompt: Optional[str] = None
build_script_code: Optional[str] = None


@dataclasses.dataclass
@@ -411,7 +413,9 @@ def _get_targets_agent(self, benchmark: str, trial: str) -> Target:
build_script_code = f.read()

# TODO(dongge): Properly show build script code in reports.
return Target(code=fuzz_target_code, fixer_prompt=build_script_code)
return Target(code=fuzz_target_code,
fixer_prompt=None,
build_script_code=build_script_code)

def get_samples(self, results: list[evaluator.Result],
targets: list[str]) -> list[Sample]:
@@ -638,7 +642,9 @@ def _create_benchmark(
function = benchmark_id.split('-')[-1]
signature = self._find_benchmark_signature(project,
function) or benchmark_id
return Benchmark(benchmark_id, status, result, signature, project, function)
language = self._find_benchmark_language(project, function)
return Benchmark(benchmark_id, status, result, signature, project, function,
language)

def _find_benchmark_signature(self, project: str,
target_function: str) -> str:
@@ -670,6 +676,23 @@ def _find_benchmark_signature(self, project: str,

return matched_prefix_signature

def _find_benchmark_language(self, project: str, target_function: str) -> str:
"""Finds the programming language of the benchmark."""
if not self._benchmark_dir:
return ''

project_path = os.path.join(self._benchmark_dir, f'{project}.yaml')
if not FileSystem(project_path).isfile():
return ''

try:
with FileSystem(project_path).open() as f:
benchmark_data = yaml.safe_load(f)
return benchmark_data.get('language', '')
except Exception as e:
logging.error('Failed to read benchmark file %s: %s', project_path, e)
return ''


def _parse_log_parts(log: str) -> list[LogPart]:
"""Parse log into parts."""
23 changes: 23 additions & 0 deletions report/templates/base.html
Original file line number Diff line number Diff line change
@@ -100,7 +100,30 @@
line-height: 1.5;
white-space: pre;
}

.hljs {
background: transparent;
padding: 0;
}
</style>
<!-- added highlight.js for syntax highlighting -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/default.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script>
<!-- some Additional language -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/bash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/c.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/cpp.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/java.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/python.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/rust.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/go.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', (event) => {
document.querySelectorAll('pre code.syntax-highlight').forEach((block) => {
hljs.highlightElement(block);
});
});
</script>
<body>
LLM: {{ model }}
{% block content %}{% endblock %}
21 changes: 16 additions & 5 deletions report/templates/sample.html
Original file line number Diff line number Diff line change
@@ -16,10 +16,10 @@
#}{% extends 'base.html' %}

{% block content %}
<h1>{{ benchmark }} / {{ sample.id }}</h1>
Bug: {{ sample.result.crashes and not sample.result.is_semantic_error }}
<h1>{{ benchmark_id }} / {{ sample.id }}</h1>
Bug: {{ sample.result.crashes|default(false) and not sample.result.is_semantic_error|default(false) }}
<br>
Crash reason: {{ sample.result.semantic_error }}
Crash reason: {{ sample.result.semantic_error|default('') }}
<br>
<br>

@@ -52,10 +52,21 @@ <h3>Final code</h3>
{% else %}
<h3>Code #{{ loop.index - 1}}</h3>
{% endif %}

{% if target.code is defined and target.code %}
<div class="code-container">
<pre class="line-numbers">{% for line in target.code|remove_trailing_empty_lines|splitlines %}<span>{{ loop.index }}</span>{% endfor %}</pre>
<pre class="code-content"><code class="syntax-highlight language-{% if benchmark.language is defined and benchmark.language %}{{ benchmark.language | lower }}{% else %}plaintext{% endif %}">{{ target.code|remove_trailing_empty_lines }}</code></pre>
</div>
{% endif %}

{% if target.build_script_code is defined and target.build_script_code %}
<h3>Build Script</h3>
<div class="code-container">
<pre class="line-numbers">{% for line in target.code.splitlines() %}<span>{{ loop.index }}</span>{% endfor %}</pre>
<pre class="code-content">{% for line in target.code.splitlines() %}<code>{{ line }}</code>{% endfor %}</pre>
<pre class="line-numbers">{% for line in target.build_script_code|remove_trailing_empty_lines|splitlines %}<span>{{ loop.index }}</span>{% endfor %}</pre>
<pre class="code-content"><code class="syntax-highlight language-bash">{{ target.build_script_code|remove_trailing_empty_lines }}</code></pre>
</div>
{% endif %}
{% endfor %}

<h2>Logs</h2>
71 changes: 63 additions & 8 deletions report/web.py
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@
import shutil
import threading
import time
import traceback
import urllib.parse
from functools import partial
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
@@ -65,6 +66,33 @@ def _cov_report_link(link: str):
return link_path + 'index.html'
return link_path + 'report.html'

@staticmethod
def _remove_trailing_empty_lines(code: str) -> str:
"""Remove trailing empty lines from code."""
if not code:
return ""

try:
lines = code.splitlines()
while lines and not lines[-1].strip():
lines.pop()

return '\n'.join(lines)
except Exception as e:
logging.warning("Error in remove_trailing_empty_lines filter: %s", e)
return code # Return original code on error

@staticmethod
def _splitlines(text: str) -> list:
"""Split text into lines, similar to Python's splitlines()."""
if not text:
return []
try:
return text.splitlines()
except Exception as e:
logging.warning("Error in splitlines filter: %s", e)
return [text] # Return original as single line on error

def __init__(self, template_globals: Optional[Dict[str, Any]] = None):
self._env = jinja2.Environment(
loader=jinja2.FileSystemLoader("report/templates"),
@@ -73,6 +101,9 @@ def __init__(self, template_globals: Optional[Dict[str, Any]] = None):
self._env.filters['urlencode_filter'] = self._urlencode_filter
self._env.filters['percent'] = self._percent
self._env.filters['cov_report_link'] = self._cov_report_link
self._env.filters[
'remove_trailing_empty_lines'] = self._remove_trailing_empty_lines
self._env.filters['splitlines'] = self._splitlines

if template_globals:
for key, val in template_globals.items():
@@ -233,18 +264,42 @@ def _write_benchmark_sample(self, benchmark: Benchmark, sample: Sample,
sample_targets: List[Target]):
"""Generate the sample page and write to filesystem."""
try:
rendered = self._jinja.render(
'sample.html',
benchmark=benchmark.id,
sample=sample,
logs=self._results.get_logs(benchmark.id, sample.id),
run_logs=self._results.get_run_logs(benchmark.id, sample.id),
triage=self._results.get_triage(benchmark.id, sample.id),
targets=sample_targets)
# Ensure all required variables are available
logs = self._results.get_logs(benchmark.id, sample.id) or []
run_logs = self._results.get_run_logs(benchmark.id, sample.id) or ""
triage = self._results.get_triage(benchmark.id, sample.id) or {
"result": "",
"triager_prompt": ""
}

rendered = self._jinja.render('sample.html',
benchmark=benchmark,
benchmark_id=benchmark.id,
sample=sample,
logs=logs,
run_logs=run_logs,
triage=triage,
targets=sample_targets)
self._write(f'sample/{benchmark.id}/{sample.id}.html', rendered)
except Exception as e:
logging.error('Failed to write sample/%s/%s:\n%s', benchmark.id,
sample.id, e)
logging.error('Exception details: %s', traceback.format_exc())
# Create a simple error page so users see something
try:
error_html = f"""
<html>
<head><title>Error rendering {benchmark.id}/{sample.id}</title></head>
<body>
<h1>Error rendering sample page</h1>
<p>An error occurred while rendering this page. Please check the logs for details.</p>
<pre>{str(e)}</pre>
</body>
</html>
"""
self._write(f'sample/{benchmark.id}/{sample.id}.html', error_html)
except Exception:
pass # Ignore errors in error handling


def generate_report(args: argparse.Namespace) -> None: