Skip to content

Crash Analyzer Agent #814

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 160 commits into from
Jun 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
160 commits
Select commit Hold shift + click to select a range
dac1f5d
fix typo
maoyixie Nov 7, 2024
688baa0
crash analyzer agent prototype
maoyixie Nov 7, 2024
0a80cea
add _execute_agent_cloud in crash analyzer
maoyixie Nov 7, 2024
0374106
save cloud testcase
maoyixie Nov 7, 2024
2e874c6
support gpt chat model
maoyixie Nov 7, 2024
3aac96d
update lldb _handle_conclusion
maoyixie Nov 7, 2024
22f639f
update prompt, compile project
maoyixie Nov 7, 2024
dec8a91
update CrashResult
maoyixie Nov 7, 2024
1b4dcb0
temp, revert later
maoyixie Nov 7, 2024
2fa0dbb
todo: fix openai prompt
maoyixie Nov 7, 2024
19491fd
add tutorial with add_problem instead of append
maoyixie Nov 7, 2024
ad73c77
try to fix invalid type for message in gpt(todo)
maoyixie Nov 7, 2024
e3f0398
matain gpt chat session, ugly fix invalid message
maoyixie Nov 7, 2024
28ca005
update
maoyixie Nov 7, 2024
8d38014
fix agent name
maoyixie Nov 7, 2024
8fe9a31
fix crash analyzer no benchmark
maoyixie Nov 7, 2024
b98bb18
reformat
maoyixie Nov 7, 2024
bbe8e9f
delete unused import
maoyixie Nov 7, 2024
4027d41
fix line to long
maoyixie Nov 7, 2024
1406f71
fix line too long
maoyixie Nov 7, 2024
953c8a0
fix warning
maoyixie Nov 7, 2024
fd3c531
fix warning
maoyixie Nov 7, 2024
6584d11
crash result inherit from run result
maoyixie Nov 7, 2024
fcdda7c
reformat
maoyixie Nov 7, 2024
2b4c529
pass run_result instead of result_history to crash analyzer execute()
maoyixie Nov 7, 2024
dbbc33c
revert to match abstract method in Resykt
maoyixie Nov 7, 2024
45e7fd8
add type check
maoyixie Nov 7, 2024
29e8fd4
fix "Prompt" must return value on all code paths
maoyixie Nov 7, 2024
0a0d5b1
fix Argument missing for parameter "crash_func"
maoyixie Nov 7, 2024
be8b629
fix Argument missing for parameter "crash_func"
maoyixie Nov 7, 2024
7840581
fix Dangerous default value {} as argument
maoyixie Nov 7, 2024
a4e40d8
fix crash_func
maoyixie Nov 7, 2024
443d33c
fix Argument missing for parameter "crash_func"
maoyixie Nov 7, 2024
b2b965e
fix RunResult in execute in crash analyzer
maoyixie Nov 7, 2024
002e936
fix some errors
maoyixie Nov 7, 2024
2a17374
fix some errors
maoyixie Nov 7, 2024
f5753f0
fix Result
maoyixie Nov 7, 2024
1914dbf
fix Result
maoyixie Nov 7, 2024
25beede
fix Function with declared return type "CrashResult" must return valu…
maoyixie Nov 7, 2024
964f01b
"benchmark" is unbound
maoyixie Nov 7, 2024
2edd59e
delete output.txt
maoyixie Nov 7, 2024
fc06b0b
fix type
maoyixie Nov 7, 2024
af6cc3f
try to fix unterminated s command, more logger info
maoyixie Nov 7, 2024
0c15edb
try to fix compile failure
maoyixie Nov 7, 2024
c52136c
try to fix -g: not a valid identifier
maoyixie Nov 7, 2024
7ea741a
try to fix command in dockerfile
maoyixie Nov 7, 2024
9cd46a0
try to fix command in dockerfile
maoyixie Nov 7, 2024
e27080f
TODO: some issues like missing artifact and lldb command failure, del…
maoyixie Nov 7, 2024
01fa45c
print more info, format
maoyixie Nov 7, 2024
c67407e
try to fix Anomalous backslash in string
maoyixie Nov 7, 2024
6c54fe7
try to fix did not copy fuzz target and build script
maoyixie Nov 7, 2024
fde8009
Delete output1.txt
maoyixie Nov 7, 2024
7aa5d5b
Delete output2.txt
maoyixie Nov 7, 2024
fe6a64d
adjust cycle times
maoyixie Nov 7, 2024
726cfdd
delete comment
maoyixie Nov 7, 2024
d2ea8fa
reformat
maoyixie Nov 7, 2024
dffdf5f
line break
maoyixie Nov 7, 2024
3906f09
optimize artifact name
maoyixie Nov 7, 2024
b2ed633
try to fix failed to match conclusion
maoyixie Nov 7, 2024
e66d849
TODO: fix TypeError: Object of type set is not JSON serializable
maoyixie Nov 7, 2024
ea9ebc1
optimize lldb prompt, try to fix lldb inconsistency
maoyixie Nov 7, 2024
552d02e
delete some info
maoyixie Nov 7, 2024
45856d3
reformat
maoyixie Nov 7, 2024
52d8f20
optimize lldb prompt, try to fix ERROR: The required directory < does…
maoyixie Nov 7, 2024
e47641e
try to fix lldb
maoyixie Nov 7, 2024
ddaa549
fix -e
maoyixie Nov 7, 2024
468d850
try to fix
maoyixie Nov 7, 2024
833de89
optimize lldb prompt, ignore insignificant errors
maoyixie Nov 7, 2024
4cac3f6
optimize lldb prompt
maoyixie Nov 7, 2024
b105395
optimize lldb prompt
maoyixie Nov 7, 2024
760907f
delete info
maoyixie Nov 7, 2024
24a48ee
fix fuzzing_language
maoyixie Nov 20, 2024
ed537d5
modify comment
maoyixie Nov 23, 2024
e95cf2d
Create temp
DonggeLiu Nov 7, 2024
f9c13f4
delete temp
maoyixie Feb 25, 2025
b01b044
fix import
maoyixie Feb 25, 2025
813596c
fix typo
maoyixie Feb 25, 2025
d47d810
fix termination condition
maoyixie Mar 1, 2025
647efa8
analysis stage
maoyixie Mar 1, 2025
2ee0f40
work dir
maoyixie Mar 1, 2025
97acb2a
cloud builder
maoyixie Mar 1, 2025
ee1fd2a
lldb
maoyixie Mar 1, 2025
8af08d7
fix crash analyzer
maoyixie Mar 1, 2025
08f1689
execution stage
maoyixie Mar 1, 2025
bbe4988
results
maoyixie Mar 1, 2025
25a04ed
add info in prototyper
maoyixie Mar 1, 2025
e63cf85
fix lint
maoyixie Mar 1, 2025
c3432b5
fix RunResult
maoyixie Mar 1, 2025
3097b1a
fix RunResult
maoyixie Mar 1, 2025
fa8ac33
fix lint, fix prompt
maoyixie Mar 2, 2025
e7e8012
delete info
maoyixie Mar 2, 2025
6be630b
fix typo
maoyixie Mar 4, 2025
c046b53
CFLAGS
maoyixie Mar 6, 2025
d7deb7e
delete JCC
maoyixie Mar 6, 2025
de7df65
rename image name
maoyixie Mar 6, 2025
d1dd2b3
recover build_result
maoyixie Mar 6, 2025
fe101f3
terminate when it is not AnalysisResult
maoyixie Mar 6, 2025
5cccf95
delete blank line
maoyixie Mar 6, 2025
7b0b0dd
assert RunResult
maoyixie Mar 6, 2025
e71a474
recover comment
maoyixie Mar 6, 2025
591a339
inherit ProjectContainerTool
maoyixie Mar 6, 2025
2937b28
detach SemanticCheckResult from RunResult
maoyixie Mar 6, 2025
ee740d0
OpenAIPrompt append
maoyixie Mar 7, 2025
5184f5b
lldbtool inherit from containertool
maoyixie Mar 7, 2025
91339cd
use trial ID to distinguish artifact folder
maoyixie Mar 7, 2025
0b6bc96
not overwrite _container_handle_bash_command
maoyixie Mar 8, 2025
ee63bd0
rename _handle_conclusion
maoyixie Mar 8, 2025
f9a7466
optimize CrashResult initialization
maoyixie Mar 8, 2025
f243e89
use original artifact name
maoyixie Mar 8, 2025
f116f6a
detach _copy_crash_file()
maoyixie Mar 8, 2025
c46c409
add trial ID to log
maoyixie Mar 10, 2025
d4f1d47
optimimze LLDBTool inherit from ProjectContainerTool
maoyixie Mar 10, 2025
e56eeec
optimize model class with chat session
maoyixie Mar 10, 2025
0edc0ae
optimize handle_lldb_command
maoyixie Mar 12, 2025
32c3b49
reuse create_ossfuzz_project
maoyixie Mar 12, 2025
c6351ef
fix lint
maoyixie Mar 13, 2025
70a0545
change create_ossfuzz_project into staticmethod, fix lint
maoyixie Mar 13, 2025
38809a0
fix lint
maoyixie Mar 13, 2025
1cfea58
fix loss t
maoyixie Mar 13, 2025
e1c67b7
Fix 'No such file or directory' error
DonggeLiu Mar 14, 2025
7aa761d
Fix 'NameError: name 'WorkDirs' is not defined'
DonggeLiu Mar 14, 2025
1a6f267
fix lint
maoyixie Mar 14, 2025
476c05e
Write fuzz target and build script to the expected locations in the n…
DonggeLiu Mar 14, 2025
69b2b4f
Fix artifact blobing
DonggeLiu Mar 20, 2025
1047a18
fix author
DonggeLiu Mar 20, 2025
fdc4b55
Fix blobbing
DonggeLiu Mar 20, 2025
ebd49dd
Various bug fixes
DonggeLiu Mar 21, 2025
4568a08
Fix initialization
DonggeLiu Mar 21, 2025
ab7f36f
code move to _copy_cloud_artifact
maoyixie Mar 24, 2025
92ac9f5
add bash tool for crash analyzer, add crash analyzer prompt
maoyixie Mar 29, 2025
4fcdbaf
fix lint
maoyixie Mar 29, 2025
c877dd7
fix wrap lldb in bash, use screen to maintain lldb session
maoyixie Mar 30, 2025
753febc
fix tool installation failure
maoyixie Apr 27, 2025
1b3ce54
implement get_chat_client
maoyixie Apr 28, 2025
87b98a6
reset FI
maoyixie Apr 28, 2025
eab7415
fix some typp
maoyixie May 5, 2025
328349e
optimize prompt to let llm use lldb
maoyixie May 6, 2025
ba7c809
optimize prompt to let llm use lldb
maoyixie May 7, 2025
7f080ea
fix syntax error: unexpected end of file
maoyixie May 8, 2025
fbbb502
disable compiler optimization
maoyixie May 11, 2025
0f9b6f8
fix lldb and screen related
maoyixie May 11, 2025
45d7d16
use script to fix no output in log
maoyixie May 11, 2025
f0e77c8
fix script command
maoyixie May 11, 2025
1cd9202
use screen flush log
maoyixie May 11, 2025
e432609
fix Unknown option Logflush
maoyixie May 11, 2025
c59e934
optimize lldb prompt
maoyixie May 11, 2025
21a2173
fix git rebase
maoyixie May 12, 2025
3f671b6
fix model
maoyixie May 12, 2025
1d88c95
fix artifact_url missing
maoyixie May 12, 2025
5d1331d
lint
maoyixie May 12, 2025
c45197c
try to fix cloud exp file not found failure
maoyixie May 21, 2025
d01e21b
possible reason
maoyixie May 22, 2025
98b8924
more debug info
maoyixie May 23, 2025
8bde106
upload artifact to gcs
maoyixie May 28, 2025
a79b121
cp to artifact_path, ensure artifact_path consistency, fix artifact p…
maoyixie Jun 1, 2025
1e4f62f
allow artifact mkdir failure
maoyixie Jun 1, 2025
4d7c8db
mount `/experiment` dir, which stores the crash artifact.
DonggeLiu Jun 1, 2025
9e6c822
`/experiment` will not exist after each step, saving it to persistent…
DonggeLiu Jun 2, 2025
2100a11
Allow cp artifact fail (for non-crash analyzer agents)
DonggeLiu Jun 2, 2025
a2d0186
Do not delete the work_dir, which may contain the artifact.
DonggeLiu Jun 2, 2025
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
201 changes: 200 additions & 1 deletion agent/crash_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,207 @@
"""An LLM agent to analyze and provide insight of a fuzz target's runtime crash.
Use it as a usual module locally, or as script in cloud builds.
"""
import argparse
import os
import subprocess as sp
from typing import Optional

import logger
from agent.base_agent import BaseAgent
from experiment import evaluator as evaluator_lib
from experiment import oss_fuzz_checkout
from experiment.workdir import WorkDirs
from llm_toolkit import prompt_builder
from llm_toolkit.models import LLM
from llm_toolkit.prompts import Prompt
from results import AnalysisResult, CrashResult, Result, RunResult
from tool.base_tool import BaseTool
from tool.container_tool import ProjectContainerTool
from tool.lldb_tool import LLDBTool

MAX_ROUND = 100


class CrashAnalyzer(BaseAgent):
pass
"""The Agent to analyze a runtime crash and provide insight to fuzz target."""

def __init__(self,
trial: int,
llm: LLM,
args: argparse.Namespace,
tools: Optional[list[BaseTool]] = None,
name: str = '',
artifact_path: str = '') -> None:
super().__init__(trial, llm, args, tools, name)
self.artifact_path = artifact_path

def _initial_prompt(self, results: list[Result]) -> Prompt:
"""Constructs initial prompt of the agent."""
last_result = results[-1]

if isinstance(last_result, RunResult):
crash_analyzer_prompt_builder = \
prompt_builder.CrashAnalyzerTemplateBuilder(
model=self.llm,
benchmark=last_result.benchmark)
prompt = crash_analyzer_prompt_builder.build_crash_analyzer_prompt(
last_result.benchmark, last_result.fuzz_target_source,
last_result.run_error, last_result.crash_func)
return prompt

logger.error("Expected a RunResult object in results list",
trial=self.trial)
return prompt_builder.CrashAnalyzerTemplateBuilder(self.llm).build([])

def _format_lldb_execution_result(
self,
lldb_command: str,
process: sp.CompletedProcess,
previous_prompt: Optional[Prompt] = None) -> str:
"""Formats a prompt based on lldb execution result."""
if previous_prompt:
previous_prompt_text = previous_prompt.get()
else:
previous_prompt_text = ''
stdout = self.llm.truncate_prompt(process.stdout,
previous_prompt_text).strip()
stderr = self.llm.truncate_prompt(process.stderr,
stdout + previous_prompt_text).strip()
return (f'<lldb command>\n{lldb_command.strip()}\n</lldb command>\n'
f'<lldb output>\n{stdout}\n</lldb output>\n'
f'<stderr>\n{stderr}\n</stderr>\n')

def _container_handle_lldb_command(self, response: str, tool: LLDBTool,
prompt: Prompt) -> Prompt:
"""Handles the command from LLM with lldb tool."""
prompt_text = ''
for command in self._parse_tags(response, 'lldb'):
process = tool.execute_in_screen(command)
prompt_text += self._format_lldb_execution_result(
command, process, previous_prompt=prompt) + '\n'
prompt.append(prompt_text)
return prompt

def _container_handle_conclusion(self, cur_round: int, response: str,
crash_result: CrashResult) -> None:
"""Parses LLM conclusion, analysis and suggestion."""
logger.info('----- ROUND %02d Received conclusion -----',
cur_round,
trial=self.trial)

conclusion = self._parse_tag(response, 'conclusion')
if conclusion == 'Crash is caused by bug in fuzz driver.':
crash_result.true_bug = False
elif conclusion == 'Crash is caused by bug in project.':
crash_result.true_bug = True
else:
logger.error('***** Failed to match conclusion in %02d rounds *****',
cur_round,
trial=self.trial)

crash_result.insight = self._parse_tag(response, 'analysis and suggestion')
if not crash_result.insight:
logger.error('Round %02d No analysis and suggestion in conclusion: %s',
cur_round,
response,
trial=self.trial)

def _container_tool_reaction(self, cur_round: int, response: str,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be the same as _container_tool_reaction() in Prototyper?
If so, we can relocate that function into base_agent so that you don't have to repeat it here.

Again, future users/editors will appreciate this because they can read/modify the code in one place.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is different from _container_tool_reaction() in Prototyper because the prompt is designed differently.

crash_result: CrashResult) -> Optional[Prompt]:
"""Validates LLM conclusion or executes its command."""
if self._parse_tag(response, 'conclusion'):
return self._container_handle_conclusion(cur_round, response,
crash_result)
prompt = prompt_builder.CrashAnalyzerTemplateBuilder(self.llm,
None).build([])
if self._parse_tag(response, 'lldb'):
return self._container_handle_lldb_command(response, self.analyze_tool,
prompt)
if self._parse_tag(response, 'bash'):
return self._container_handle_bash_command(response, self.check_tool,
prompt)
return None

def execute(self, result_history: list[Result]) -> AnalysisResult:
"""Executes the agent based on previous run result."""
WorkDirs(self.args.work_dirs.base, keep=True)
last_result = result_history[-1]
benchmark = last_result.benchmark
logger.info('Executing Crash Analyzer', trial=self.trial)
assert isinstance(last_result, RunResult)

if not os.path.exists(last_result.artifact_path):
logger.error('Artifact path %s does not exist',
last_result.artifact_path,
trial=self.trial)
Copy link
Collaborator

@DonggeLiu DonggeLiu May 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good news:
We no longer see the error /workspace/crash-XXXX does not exist, hence the artifact seems to be correctly uploaded.

Bad news:
This error log was printed:

Already have image (with digest): gcr.io/cloud-builders/docker
2025-05-28 09:29:13 [Trial ID: 00] INFO [logger.info]: Checkign if we should use local FI
2025-05-28 09:29:13 [Trial ID: 00] INFO [logger.info]: This does not require a local FI.
2025-05-28 09:29:13 [Trial ID: 07] INFO [logger.info]: Executing Crash Analyzer
2025-05-28 09:29:13 [Trial ID: 07] ERROR [logger.error]: Artifact path /experiment/results/output-ada-url-ada_can_parse_with_base/artifacts/07.fuzz_target-F0-07/crash-5c013da7b11b7ccb2c437239fbcdbf4c53b20655 does not exist
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/workspace/ofg/agent/base_agent.py", line 280, in <module>
    BaseAgent.cloud_main()
  File "/workspace/ofg/agent/base_agent.py", line 266, in cloud_main
    result = agent.execute(result_history)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/ofg/agent/crash_analyzer.py", line 172, in execute
    evaluator_lib.Evaluator.create_ossfuzz_project_with_lldb(
  File "/workspace/ofg/experiment/evaluator.py", line 321, in create_ossfuzz_project_with_lldb
    shutil.copyfile(
  File "/usr/lib/python3.11/shutil.py", line 256, in copyfile
    with open(src, 'rb') as fsrc:
         ^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: '/experiment/results/output-ada-url-ada_can_parse_with_base/artifacts/07.fuzz_target-F0-07/crash-5c013da7b11b7ccb2c437239fbcdbf4c53b20655'

Would the artifact be at /workspace/crash-XXXX, because the cloud build step copied the artifact there and crash analyzer no longer executes self._copy_cloud_artifact(last_result.artifact_path)?

IIRC, we discussed that it's better to place the artifact at last_result.artifact_path (in this case, /experiment/results/output-ada-url-ada_can_parse_with_base/artifacts/07.fuzz_target-F0-07/crash-5c013da7b11b7ccb2c437239fbcdbf4c53b20655), because it is:

  1. Transparent: It will be the same path for both local and cloud exp.
  2. Straightforward: we can always trust last_result.artifact_path.

Thinking more about this:
Would it be easier to directly download the artifact to last_result.artifact_path (instead of /workspace/artifact-XXX) in the cloud build step?
The agent does not need to copy it, it can safely assume the artifact is at last_result.artifact_path for both local and cloud setup.
We may need to add a step to create the parent dir of last_result.artifact_path in the cloud build before download it.


# TODO(dongge): Move these to oss_fuzz_checkout.
generated_target_name = os.path.basename(benchmark.target_path)
sample_id = os.path.splitext(generated_target_name)[0]
generated_oss_fuzz_project = (
f'{benchmark.id}-{sample_id}-lldb-{self.trial:02d}')
generated_oss_fuzz_project = oss_fuzz_checkout.rectify_docker_tag(
generated_oss_fuzz_project)

# TODO(dongge): Write to OSS-Fuzz project dir files directly.
fuzz_target_path = os.path.join(last_result.work_dirs.fuzz_targets,
f'{self.trial:02d}.fuzz_target')
with open(fuzz_target_path, 'w') as ft_file:
ft_file.write(last_result.fuzz_target_source)
if last_result.build_script_source:
build_script_path = os.path.join(last_result.work_dirs.fuzz_targets,
f'{self.trial:02d}.build_script')
with open(build_script_path, 'w') as ft_file:
ft_file.write(last_result.build_script_source)
else:
build_script_path = ''

evaluator_lib.Evaluator.create_ossfuzz_project_with_lldb(
benchmark, generated_oss_fuzz_project, fuzz_target_path, last_result,
build_script_path, last_result.artifact_path)

self.analyze_tool = LLDBTool(benchmark,
result=last_result,
name='lldb',
project_name=generated_oss_fuzz_project)
self.analyze_tool.execute('compile > /dev/null')
# Launch LLDB and load fuzz target binary
self.analyze_tool.execute(f'screen -dmS lldb_session -L '
f'-Logfile /tmp/lldb_log.txt '
f'lldb /out/{last_result.benchmark.target_name}')
self.check_tool = ProjectContainerTool(
benchmark, name='check', project_name=generated_oss_fuzz_project)
self.check_tool.compile(extra_commands=' && rm -rf /out/* > /dev/null')
prompt = self._initial_prompt(result_history)
prompt.add_problem(self.analyze_tool.tutorial())
prompt.add_problem(self.check_tool.tutorial())
crash_result = CrashResult(benchmark=benchmark,
trial=last_result.trial,
work_dirs=last_result.work_dirs,
author=self,
chat_history={self.name: ''})
cur_round = 1
try:
client = self.llm.get_chat_client(model=self.llm.get_model())
while prompt and cur_round < MAX_ROUND:
response = self.chat_llm(cur_round=cur_round,
client=client,
prompt=prompt,
trial=self.trial)
prompt = self._container_tool_reaction(cur_round, response,
crash_result)
cur_round += 1
self._sleep_random_duration(trial=self.trial)
finally:
# Cleanup: stop the container
logger.debug('Stopping the crash analyze container %s',
self.analyze_tool.container_id,
trial=self.trial)
self.analyze_tool.terminate()

analysis_result = AnalysisResult(
author=self,
run_result=last_result,
crash_result=crash_result,
chat_history={self.name: crash_result.to_dict()})
return analysis_result
78 changes: 70 additions & 8 deletions common/cloud_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

import utils
from agent.base_agent import BaseAgent
from results import Result
from results import Result, RunResult

OF_REPO = 'https://github.com/google/oss-fuzz.git'
OFG_ROOT_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
Expand Down Expand Up @@ -82,11 +82,42 @@ def __init__(self, args: argparse.Namespace) -> None:
def _upload_files(self, archive_name: str, target_dir: str,
files_to_upload: list[str]) -> str:
"""Archive and upload files to GCS."""
valid_files = []
for f in files_to_upload:
file_path = os.path.join(target_dir, f)
if os.path.exists(file_path):
valid_files.append(f)
else:
logging.error("File does not exist: %s", file_path)

valid_files.sort()

with tempfile.TemporaryDirectory() as tmpdirname:
archive_path = os.path.join(tmpdirname, archive_name)
tar_command = ['tar', '-czf', archive_path] + files_to_upload
subprocess.run(tar_command, cwd=target_dir, check=True)
logging.info('Created archive: %s', archive_path)
tar_command = ['tar', '-czf', archive_path] + valid_files
logging.error("Archive path: %s (exists: %s)", archive_path,
os.path.exists(archive_path))
logging.error("Tar command: %s", ' '.join(tar_command))

try:
result = subprocess.run(tar_command,
cwd=target_dir,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True)
logging.error("subprocess stdout:\n%s", result.stdout)
logging.error("subprocess stderr:\n%s", result.stderr)
except subprocess.CalledProcessError as e:
logging.error("Tar command failed with return code %d", e.returncode)
logging.error("stdout:\n%s", e.stdout)
logging.error("stderr:\n%s", e.stderr)
raise

if os.path.exists(archive_path):
logging.info("Successfully created archive: %s", archive_path)
else:
logging.error("Failed to create archive: %s", archive_path)
return self._upload_to_gcs(archive_path)

def _upload_to_gcs(self, local_file_path: str) -> str:
Expand Down Expand Up @@ -149,7 +180,8 @@ def _upload_fi_oss_fuzz_data(self) -> str:
files_to_upload)

def _request_cloud_build(self, ofg_repo_url: str, agent_dill_url: str,
results_dill_url: str, oss_fuzz_data_url: str,
results_dill_url: str, artifact_url: str,
artifact_path: str, oss_fuzz_data_url: str,
data_dir_url: str, new_result_filename: str) -> str:
"""Requests Cloud Build to execute the operation."""

Expand All @@ -167,7 +199,7 @@ def _request_cloud_build(self, ofg_repo_url: str, agent_dill_url: str,

cloud_build_config = {
'steps': [
# Step 1: Download the dill files from GCS bucket.
# Step 1: Download the dill and artifact files from GCS bucket.
{
'name': 'bash',
'dir': '/workspace',
Expand All @@ -183,6 +215,23 @@ def _request_cloud_build(self, ofg_repo_url: str, agent_dill_url: str,
'dir': '/workspace',
'args': ['cp', results_dill_url, 'dills/result_history.pkl']
},
{
'name': 'gcr.io/cloud-builders/gsutil',
'entrypoint': 'bash',
'args': [
'-c',
f'mkdir -p /workspace/host/{os.path.dirname(artifact_path)}'
],
'allowFailure': True,
},
{
'name': 'gcr.io/cloud-builders/gsutil',
'dir': '/workspace',
'args': [
'cp', artifact_url, f'/workspace/host/{artifact_path}'
],
'allowFailure': True,
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example, before this step, we have something like:

            {
                'name':
                    'gcr.io/cloud-builders/gsutil',
                'entrypoint':
                    'bash',
                'args': [
                    '-c', f'mkdir -p {os.path.dirname(artifact_path)}'
                ]
            },

# Step 2: Prepare OFG and OF repos.
{
'name':
Expand Down Expand Up @@ -256,6 +305,8 @@ def _request_cloud_build(self, ofg_repo_url: str, agent_dill_url: str,
'-v',
'/workspace:/workspace',
'-v',
'/workspace/host/experiment:/experiment',
'-v',
'/var/run/docker.sock:/var/run/docker.sock',
'-e',
'VERTEX_AI_LOCATIONS=' +
Expand All @@ -275,7 +326,7 @@ def _request_cloud_build(self, ofg_repo_url: str, agent_dill_url: str,
'/workspace/dills/new_result.pkl'
],
},
# Step 4: Upload the result to GCS bucket
# Step 6: Upload the result to GCS bucket
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

{
'name': 'bash',
'dir': '/workspace',
Expand Down Expand Up @@ -388,12 +439,23 @@ def run(self, agent: BaseAgent, result_history: list[Result],
ofg_url = self._prepare_and_upload_archive(result_history)
agent_url = self._upload_to_gcs(agent_dill)
results_url = self._upload_to_gcs(results_dill)
artifact_url = ''
artifact_path = ''
if isinstance(result_history[-1], RunResult):
artifact_path = result_history[-1].artifact_path
if artifact_path:
logging.info('Found artifact_path: %s in RunResult.', artifact_path)
artifact_url = self._upload_to_gcs(artifact_path)
logging.info('Uploaded artifact to %s', artifact_url)
else:
logging.error('No artifact_path found in RunResult.')
oss_fuzz_data_url = self._upload_oss_fuzz_data()
data_dir_url = self._upload_fi_oss_fuzz_data()

# Step 3: Request Cloud Build.
new_result_filename = f'{uuid.uuid4().hex}.pkl'
build_id = self._request_cloud_build(ofg_url, agent_url, results_url,
artifact_url, artifact_path,
oss_fuzz_data_url, data_dir_url,
new_result_filename)

Expand All @@ -416,7 +478,7 @@ def run(self, agent: BaseAgent, result_history: list[Result],

cloud_build_log += self._get_build_log(build_id)

# Step 4: Deserialize dilld file.
# Step 5: Deserialize dilld file.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼

result = utils.deserialize_from_dill(new_result_dill)
if not result:
cloud_build_log += f'Failed to deserialize from dill {new_result_dill}.\n'
Expand Down
Loading