@@ -100,11 +100,20 @@ def command_for(
100100 jar : pathlib .Path ,
101101 result_file : pathlib .Path ,
102102 config : dict [str , Any ],
103+ runtime_temp_dir : pathlib .Path ,
103104 command_prefix : list [str ] | None = None ,
104105) -> list [str ]:
105106 jmh = config ["jmh" ]
106- fork_jvm_args = jmh .get ("jvm_args" , [])
107- command = [* (command_prefix or []), java , "-jar" , str (jar ), jmh ["include" ]]
107+ runtime_temp_arg = f"-Djava.io.tmpdir={ runtime_temp_dir } "
108+ fork_jvm_args = [* jmh .get ("jvm_args" , []), runtime_temp_arg ]
109+ command = [
110+ * (command_prefix or []),
111+ java ,
112+ runtime_temp_arg ,
113+ "-jar" ,
114+ str (jar ),
115+ jmh ["include" ],
116+ ]
108117 command .extend (
109118 [
110119 "-bm" ,
@@ -194,6 +203,36 @@ def prepare_untrusted_result(path: pathlib.Path, file_user: str) -> None:
194203 )
195204
196205
206+ def prepare_runtime_temp (path : pathlib .Path , file_user : str | None ) -> None :
207+ if path .exists ():
208+ raise FileExistsError (f"JMH runtime temp directory already exists: { path } " )
209+ if file_user is None :
210+ path .mkdir (mode = 0o700 )
211+ return
212+ subprocess .run (
213+ [
214+ "sudo" ,
215+ "install" ,
216+ "-d" ,
217+ "--owner" ,
218+ file_user ,
219+ "--group" ,
220+ file_user ,
221+ "--mode" ,
222+ "0700" ,
223+ "--" ,
224+ str (path ),
225+ ],
226+ check = True ,
227+ )
228+ writable = subprocess .run (
229+ ["sudo" , "-H" , "-u" , file_user , "--" , "test" , "-w" , str (path )],
230+ check = False ,
231+ )
232+ if writable .returncode != 0 :
233+ raise RuntimeError (f"candidate user { file_user } cannot write JMH runtime temp { path } " )
234+
235+
197236def reclaim_untrusted_result (path : pathlib .Path , file_user : str ) -> None :
198237 # A candidate can daemonize while retaining an open writable descriptor. Kill every
199238 # process under the dedicated UID before making the result immutable and hashing it.
@@ -298,6 +337,9 @@ def run(args: argparse.Namespace) -> int:
298337 }
299338
300339 output_dir = args .output_dir .resolve ()
340+ runtime_temp_root = args .runtime_temp_root .resolve (strict = True )
341+ if not runtime_temp_root .is_dir ():
342+ raise NotADirectoryError (runtime_temp_root )
301343 raw_dir = output_dir / "raw"
302344 log_dir = output_dir / "logs"
303345 raw_dir .mkdir (parents = True , exist_ok = True )
@@ -340,6 +382,7 @@ def run(args: argparse.Namespace) -> int:
340382 "github_runner_name" : os .environ .get ("RUNNER_NAME" ),
341383 "github_run_id" : os .environ .get ("GITHUB_RUN_ID" ),
342384 "github_run_attempt" : os .environ .get ("GITHUB_RUN_ATTEMPT" ),
385+ "runtime_temp_root" : str (runtime_temp_root ),
343386 },
344387 "jmh" : effective_config ["jmh" ],
345388 "ci_overrides" : {key : value for key , value in overrides .items () if value is not None },
@@ -371,11 +414,17 @@ def run(args: argparse.Namespace) -> int:
371414 log_file .chmod (0o600 )
372415 if isolated_candidate :
373416 prepare_untrusted_result (result_file , candidate_file_user )
417+ runtime_temp_dir = runtime_temp_root / stem
418+ prepare_runtime_temp (
419+ runtime_temp_dir ,
420+ candidate_file_user if isolated_candidate else None ,
421+ )
374422 command = command_for (
375423 args .java ,
376424 jar ,
377425 result_file ,
378426 effective_config ,
427+ runtime_temp_dir ,
379428 candidate_prefix if isolated_candidate else None ,
380429 )
381430 started_at = utc_now ()
@@ -402,7 +451,13 @@ def run(args: argparse.Namespace) -> int:
402451 validation_error = None
403452 try :
404453 if run_succeeded :
405- verify_fork_jvm_args (log_file , effective_config ["jmh" ].get ("jvm_args" , []))
454+ verify_fork_jvm_args (
455+ log_file ,
456+ [
457+ * effective_config ["jmh" ].get ("jvm_args" , []),
458+ f"-Djava.io.tmpdir={ runtime_temp_dir } " ,
459+ ],
460+ )
406461 fork_jvm_args_verified = True
407462 verify_jars_unchanged (expected_jar_hashes )
408463 except ValueError as error :
@@ -427,6 +482,7 @@ def run(args: argparse.Namespace) -> int:
427482 "result_sha256" : sha256_file (result_file ) if result_file .is_file () else None ,
428483 "log_file" : log_file .relative_to (output_dir ).as_posix (),
429484 "log_sha256" : sha256_file (log_file ),
485+ "runtime_temp_dir" : str (runtime_temp_dir ),
430486 "command" : command ,
431487 }
432488 manifest ["executions" ].append (record )
@@ -451,6 +507,7 @@ def parse_args() -> argparse.Namespace:
451507 parser .add_argument ("--candidate-sha" , required = True )
452508 parser .add_argument ("--config" , type = pathlib .Path , required = True )
453509 parser .add_argument ("--output-dir" , type = pathlib .Path , required = True )
510+ parser .add_argument ("--runtime-temp-root" , type = pathlib .Path , required = True )
454511 parser .add_argument ("--java" , default = "java" )
455512 parser .add_argument ("--forks" , type = int )
456513 parser .add_argument ("--warmup-iterations" , type = int )
0 commit comments