functiontrace 0.5.2__tar.gz → 0.5.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: functiontrace
3
- Version: 0.5.2
3
+ Version: 0.5.3
4
4
  Summary: A profiling tool that traces your application's execution
5
5
  Author: Alistair Dobke
6
6
  Author-email: Matt Bryant <functiontrace@programsareproofs.com>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: functiontrace
3
- Version: 0.5.2
3
+ Version: 0.5.3
4
4
  Summary: A profiling tool that traces your application's execution
5
5
  Author: Alistair Dobke
6
6
  Author-email: Matt Bryant <functiontrace@programsareproofs.com>
@@ -11,4 +11,5 @@ functiontrace.egg-info/SOURCES.txt
11
11
  functiontrace.egg-info/dependency_links.txt
12
12
  functiontrace.egg-info/entry_points.txt
13
13
  functiontrace.egg-info/top_level.txt
14
- rust/lib.rs
14
+ rust/lib.rs
15
+ rust/multiprocessing.sh.tpl
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "functiontrace"
3
- version = "0.5.2"
3
+ version = "0.5.3"
4
4
  description = "A profiling tool that traces your application's execution"
5
5
  authors = [
6
6
  { name = "Matt Bryant", email = "functiontrace@programsareproofs.com"},
@@ -241,6 +241,8 @@ mod _functiontrace_rs {
241
241
  module: &Bound<'_, PyModule>,
242
242
  output_directory: OsString,
243
243
  ) -> Result<()> {
244
+ use std::os::unix::fs::{DirBuilderExt, PermissionsExt};
245
+
244
246
  // The name of the env var we use to notify subprocesses that there's a trace in progress.
245
247
  const BREADCRUMB: &str = "FUNCTIONTRACE_LIVE";
246
248
 
@@ -337,7 +339,16 @@ mod _functiontrace_rs {
337
339
  e => e.wrap_err("Failed to launch functiontrace-server")?,
338
340
  };
339
341
 
340
- format!("/tmp/functiontrace-server.sock.{}", server.id()).into()
342
+ let sockaddr = format!("/tmp/functiontrace-server.sock.{}", server.id());
343
+
344
+ // Also allocate a directory for storing wrappers (multiprocessing.set_executable,
345
+ // etc) we may need.
346
+ std::fs::DirBuilder::new()
347
+ .recursive(true)
348
+ .mode(0o700)
349
+ .create(format!("{sockaddr}.wrappers"))
350
+ .wrap_err("Failed to create wrapper directory")?;
351
+ sockaddr.into()
341
352
  };
342
353
 
343
354
  Python::attach(|_| {
@@ -414,6 +425,61 @@ mod _functiontrace_rs {
414
425
  .wrap_err("Failed to close initialization socket")?;
415
426
  }
416
427
 
428
+ // `multiprocessing` uses an absolute path to an executable to launch forkserver workers
429
+ // using `-c` for the actual code,
430
+ // Override this with our own wrapper that also sets up functiontrace.
431
+ Python::attach(|py| -> Result<()> {
432
+ let executable: String = py.import("sys")?.getattr("executable")?.extract()?;
433
+ let wrapper = {
434
+ // We'll store the wrappers next to the socket.
435
+ // TODO: Ideally we'd have these all in the same tmpdir, but that requires changing
436
+ // client and server code so would be a breaking change.
437
+ let mut directory = SOCKADDR
438
+ .get()
439
+ .expect("sockaddr is set during server initialization")
440
+ .clone();
441
+ directory.push(".wrappers");
442
+
443
+ std::path::PathBuf::from(directory)
444
+ .join("multiprocessing")
445
+ .join(executable.strip_prefix("/").unwrap_or(&executable))
446
+ };
447
+
448
+ // Create a multiprocessing wrapper for the current executable if it doesn't exist yet.
449
+ std::fs::create_dir_all(
450
+ wrapper
451
+ .parent()
452
+ .expect("Wrappers are always in a subdirectory"),
453
+ )
454
+ .wrap_err("Failed to create multiprocessing wrapper directories")?;
455
+
456
+ match std::fs::OpenOptions::new()
457
+ .write(true)
458
+ .create_new(true)
459
+ .open(&wrapper)
460
+ {
461
+ Ok(mut file) => {
462
+ let script = include_str!("multiprocessing.sh.tpl").replace(
463
+ "@PYTHON_INTERPRETER@",
464
+ // NOTE: This is equivalent to shlex.quote
465
+ &format!("'{}'", executable.replace('\'', "'\"'\"'")),
466
+ );
467
+
468
+ file.write_all(script.as_bytes())
469
+ .wrap_err("Failed to write multiprocessing wrapper")?;
470
+ file.set_permissions(std::fs::Permissions::from_mode(0o700))
471
+ .wrap_err("Failed to `chmod +x` multiprocessing wrapper")?;
472
+ }
473
+ Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {}
474
+ Err(err) => return Err(err).wrap_err("Failed to create multiprocessing wrapper"),
475
+ }
476
+ py.import("multiprocessing")?
477
+ .getattr("set_executable")?
478
+ .call1((wrapper,))?;
479
+ Ok(())
480
+ })
481
+ .wrap_err("Failed to configure multiprocessing tracing")?;
482
+
417
483
  ////////////////////////////////////////////////////////////////////////////
418
484
  // Teardown Configuration
419
485
  ////////////////////////////////////////////////////////////////////////////
@@ -0,0 +1,53 @@
1
+ #!/bin/sh
2
+
3
+ # Multiprocessing passes the code to execute in workers as `-c <code>`.
4
+ # Strip that out and run tracing setup first, maintaining the order of arguments just in case.
5
+
6
+ remaining=$#
7
+ while [ "$remaining" -gt 0 ]; do
8
+ arg=$1
9
+ shift
10
+ remaining=$((remaining - 1))
11
+ case "$arg" in
12
+ -W|-X)
13
+ # These arguments can consume other arguments. Skip them so we don't parse the wrong `-c`.
14
+ value=$1
15
+ shift
16
+ remaining=$((remaining - 1))
17
+ set -- "$@" "$arg" "$value"
18
+ ;;
19
+ -c)
20
+ code=$1
21
+ shift
22
+ remaining=$((remaining - 1))
23
+ set -- "$@" -c '
24
+ exec(
25
+ (
26
+ compile(
27
+ __import__("sys").argv.pop(1),
28
+ "<string>",
29
+ "exec",
30
+ dont_inherit=True,
31
+ ),
32
+ # Start tracing before exec runs the compiled code.
33
+ __import__("_functiontrace_rs").begin_tracing("."),
34
+ )[0]
35
+ )
36
+ ' "$code"
37
+ break
38
+ ;;
39
+ *)
40
+ set -- "$@" "$arg"
41
+ ;;
42
+ esac
43
+ done
44
+
45
+ # Consume the rest of the args without looking.
46
+ while [ "$remaining" -gt 0 ]; do
47
+ arg=$1
48
+ shift
49
+ remaining=$((remaining - 1))
50
+ set -- "$@" "$arg"
51
+ done
52
+
53
+ exec @PYTHON_INTERPRETER@ "$@"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes