functiontrace 0.5.0__tar.gz → 0.5.1__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.0
3
+ Version: 0.5.1
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>
@@ -5,14 +5,24 @@ fn main() -> std::io::Result<()> {
5
5
  let pyproject = std::fs::read_to_string("pyproject.toml")?
6
6
  .parse::<toml::Table>()
7
7
  .expect("pyproject.toml is valid toml");
8
- let version = pyproject
8
+ let project = pyproject
9
9
  .get("project")
10
10
  .and_then(|p| p.as_table())
11
- .and_then(|p| p.get("version"))
11
+ .expect("project field is present in pyproject.toml");
12
+
13
+ let version = project
14
+ .get("version")
12
15
  .and_then(|v| v.as_str())
13
16
  .expect("version field is present in pyproject.toml");
14
17
  println!("cargo::rustc-env=PACKAGE_VERSION={}", version);
15
18
 
19
+ let min_python = project
20
+ .get("requires-python")
21
+ .and_then(|v| v.as_str())
22
+ .and_then(|v| v.strip_prefix(">=3."))
23
+ .expect("requires-python field is present in pyproject.toml");
24
+ println!("cargo::rustc-env=MIN_PYTHON_VERSION={}", min_python);
25
+
16
26
  // Figure out which Python version we're building for
17
27
  pyo3_build_config::use_pyo3_cfgs();
18
28
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: functiontrace
3
- Version: 0.5.0
3
+ Version: 0.5.1
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>
@@ -27,7 +27,8 @@ def setup_dependencies():
27
27
  # Generate wrappers for the various Python versions we support to ensure
28
28
  # they're included in our PATH.
29
29
  wrap_pythons = ["python", "python3"] + [
30
- "python3.{}".format(minor) for minor in range(6, 13)
30
+ "python3.{}".format(minor)
31
+ for minor in range(rust._min_python_version, rust._max_python_version + 1)
31
32
  ]
32
33
  for python in wrap_pythons:
33
34
  with open(os.path.join(tempdir, python), "w") as f:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "functiontrace"
3
- version = "0.5.0"
3
+ version = "0.5.1"
4
4
  description = "A profiling tool that traces your application's execution"
5
5
  authors = [
6
6
  { name = "Matt Bryant", email = "functiontrace@programsareproofs.com"},
@@ -195,10 +195,20 @@ mod _functiontrace_rs {
195
195
  /// Initialization code run on this module's import (mostly useful for adding constants)
196
196
  #[pymodule_init]
197
197
  fn init(m: &Bound<'_, PyModule>) -> PyResult<()> {
198
- // Arbitrary code to run at the module initialization
199
198
  m.add("__version__", env!("PACKAGE_VERSION"))
200
199
  .wrap_err("Failed to expose PACKAGE_VERSION")?;
201
200
 
201
+ // Expose the minimum and maximum Python 3 versions we support
202
+ m.add(
203
+ "_min_python_version",
204
+ env!("MIN_PYTHON_VERSION")
205
+ .parse::<u32>()
206
+ .wrap_err("Failed to convert MIN_PYTHON_VERSION to int")?,
207
+ )
208
+ .wrap_err("Failed to expose _min_python_version")?;
209
+ m.add("_max_python_version", 13)
210
+ .wrap_err("Failed to expose _max_python_version")?;
211
+
202
212
  color_eyre::install().wrap_err("Failed to install color_eyre")?;
203
213
 
204
214
  // If someone else has already installed a tracing subscriber, we'll fail to initialize
@@ -244,6 +254,57 @@ mod _functiontrace_rs {
244
254
  return Err(eyre!("FunctionTrace is already initialized!"));
245
255
  }
246
256
 
257
+ // Store the processes' args, since threads will need it as part of the registration message.
258
+ ARGV.set({
259
+ // Parse args out of `sys.argv`, since it excludes the Python executable and shows the
260
+ // script's path first. This is much more complicated than using `std::env::args`, but
261
+ // is more practical.
262
+ Python::with_gil(|py| -> Result<String> {
263
+ let argv = py
264
+ .import("sys")
265
+ .and_then(|sys| sys.getattr("argv"))
266
+ .wrap_err("Failed to access sys.argv")?;
267
+
268
+ let args = argv
269
+ .downcast::<pyo3::types::PyList>()
270
+ .map_err(|e| eyre!("Failed to read sys.argv as list: {}", e))?;
271
+
272
+ let mut full_args = args.iter().fold(String::new(), |s, arg| {
273
+ // Fetch the next arg as a string if possible.
274
+ let arg = arg
275
+ .downcast::<pyo3::types::PyString>()
276
+ .map_err(|e| eyre!("Failed to read sys.argv[x] as string: {}", e))
277
+ .and_then(|arg| {
278
+ arg.to_str()
279
+ .wrap_err("Failed to read Rust string out of PyString")
280
+ })
281
+ .unwrap_or("<UNKNOWN>");
282
+
283
+ s + " " + arg
284
+ });
285
+
286
+ // We'll display this in the UI, so ensure it's under (the rather
287
+ // arbitrary) 100 chars.
288
+ if full_args.chars().count() > 95 {
289
+ full_args.truncate(95);
290
+ full_args.push_str(" ...");
291
+ }
292
+
293
+ // We'll be cloning this
294
+ full_args.shrink_to_fit();
295
+ Ok(full_args)
296
+ })
297
+ .unwrap_or_else(|_err| {
298
+ tracing::warn!("Failed to read ARGV");
299
+
300
+ "<UNKNOWN>".into()
301
+ })
302
+ })
303
+ .expect("ARGV is only set in `trace_initialization`");
304
+
305
+ /////////////////////////////////////////////////////////////////////////
306
+ // Server initialization
307
+ /////////////////////////////////////////////////////////////////////////
247
308
  // Check if we're in a subprocess of a command being run under functiontrace. If we are,
248
309
  // we should connect to the same socket. Otherwise, we should setup the full profiling
249
310
  // configuration ourselves.
@@ -253,155 +314,105 @@ mod _functiontrace_rs {
253
314
  SOCKADDR
254
315
  .set(existing_sockaddr)
255
316
  .expect("sockaddr is only set in `trace_initialization`");
256
- return Ok(());
257
- }
317
+ } else {
318
+ // Launch the functiontrace-server as a daemon, then record the address of the socket we'll
319
+ // need to communicate with it on.
320
+ let sockaddr = {
321
+ let server = match std::process::Command::new("functiontrace-server")
322
+ .arg("--directory")
323
+ .arg(output_directory)
324
+ .spawn()
325
+ {
326
+ Ok(server) => server,
327
+ Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
328
+ eprintln!("Unable to find `functiontrace-server` in the current $PATH.");
329
+ eprintln!(
330
+ "See https://functiontrace.com#installation for installation instructions."
331
+ );
258
332
 
259
- // Launch the functiontrace-server as a daemon, then record the address of the socket we'll
260
- // need to communicate with it on.
261
- let sockaddr = {
262
- let server = match std::process::Command::new("functiontrace-server")
263
- .arg("--directory")
264
- .arg(output_directory)
265
- .spawn()
266
- {
267
- Ok(server) => server,
268
- Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
269
- eprintln!("Unable to find `functiontrace-server` in the current $PATH.");
270
- eprintln!(
271
- "See https://functiontrace.com#installation for installation instructions."
272
- );
333
+ // Exit here rather than returning an error, since that'll emit a confusing
334
+ // Python stacktrace.
335
+ std::process::exit(1);
336
+ }
337
+ e => e.wrap_err("Failed to launch functiontrace-server")?,
338
+ };
273
339
 
274
- // Exit here rather than returning an error, since that'll emit a confusing
275
- // Python stacktrace.
276
- std::process::exit(1);
277
- }
278
- e => e.wrap_err("Failed to launch functiontrace-server")?,
340
+ format!("/tmp/functiontrace-server.sock.{}", server.id()).into()
279
341
  };
280
342
 
281
- format!("/tmp/functiontrace-server.sock.{}", server.id()).into()
282
- };
343
+ Python::with_gil(|_| {
344
+ // Register an env var so subprocesses know how to connect to our
345
+ // profile.
346
+ unsafe {
347
+ // SAFETY: `set_var` can only be done while no one else is reading or writing to the
348
+ // environment. We're holding the GIL, so this _should_ be true, but there's no real
349
+ // way to enforce this.
350
+ //
351
+ // This doesn't seem to blow up in C even when we don't have the GIL, so
352
+ // it's hopefully good enough in practice since there's not really an alternative.
353
+ std::env::set_var(BREADCRUMB, &sockaddr);
354
+ }
355
+ });
283
356
 
284
- Python::with_gil(|_| {
285
- // Register an env var so subprocesses know how to connect to our
286
- // profile.
287
- unsafe {
288
- // SAFETY: `set_var` can only be done while no one else is reading or writing to the
289
- // environment. We're holding the GIL, so this _should_ be true, but there's no real
290
- // way to enforce this.
291
- //
292
- // This doesn't seem to blow up in C even when we don't have the GIL, so
293
- // it's hopefully good enough in practice since there's not really an alternative.
294
- std::env::set_var(BREADCRUMB, &sockaddr);
295
- }
296
- });
357
+ // Store the server's address so our threads know how to talk to it.
358
+ SOCKADDR
359
+ .set(sockaddr)
360
+ .expect("sockaddr is only set in `trace_initialization`");
297
361
 
298
- // Store the server's address so our threads know how to talk to it.
299
- SOCKADDR
300
- .set(sockaddr)
301
- .expect("sockaddr is only set in `trace_initialization`");
302
-
303
- // Send an initialization message to the server with some information about us. This will
304
- // only be sent once, and all future threads/subprocesses will be associated underneath us.
305
- let init = {
306
- let argv = {
307
- // Parse args out of `sys.argv`, since it excludes the Python executable and shows
308
- // the script's path first. This is much more complicated than using
309
- // `std::env::args`, but is more practical.
310
- let args_prefix = Python::with_gil(|py| -> Result<String> {
311
- let argv = py
312
- .import("sys")
313
- .and_then(|sys| sys.getattr("argv"))
314
- .wrap_err("Failed to access sys.argv")?;
315
-
316
- let args = argv
317
- .downcast::<pyo3::types::PyList>()
318
- .map_err(|e| eyre!("Failed to read sys.argv as list: {}", e))?;
319
-
320
- let mut full_args = args.iter().fold(String::new(), |s, arg| {
321
- // Fetch the next arg as a string if possible.
322
- let arg = arg
323
- .downcast::<pyo3::types::PyString>()
324
- .map_err(|e| eyre!("Failed to read sys.argv[x] as string: {}", e))
325
- .and_then(|arg| {
326
- arg.to_str()
327
- .wrap_err("Failed to read Rust string out of PyString")
328
- })
329
- .unwrap_or("<UNKNOWN>");
330
-
331
- s + " " + arg
332
- });
333
-
334
- // We'll display this in the UI, so ensure it's under (the rather
335
- // arbitrary) 100 chars.
336
- if full_args.chars().count() > 95 {
337
- full_args.truncate(95);
338
- full_args.push_str(" ...");
339
- }
362
+ // Send an initialization message to the server with some information about us. This will
363
+ // only be sent once, and all future threads/subprocesses will be associated underneath us.
364
+ let init = {
365
+ let python_version = Python::with_gil(|py| {
366
+ let version = py.version_info();
367
+
368
+ format!(
369
+ "Python {}.{}.{}",
370
+ version.major, version.minor, version.patch
371
+ )
372
+ });
340
373
 
341
- // We'll be cloning this
342
- full_args.shrink_to_fit();
343
- Ok(full_args)
374
+ let platform = Python::with_gil(|py| -> Result<String> {
375
+ let platform = py
376
+ .import("sys")
377
+ .and_then(|sys| sys.getattr("platform"))
378
+ .wrap_err("Failed to access sys.platform")?;
379
+
380
+ Ok(platform
381
+ .downcast::<pyo3::types::PyString>()
382
+ .map_err(|e| eyre!("Failed to read sys.platform as string: {}", e))?
383
+ .to_str()
384
+ .wrap_err("Failed to read Rust string out of PyString")?
385
+ .into())
344
386
  })
345
387
  .unwrap_or_else(|_err| {
346
- tracing::warn!("Failed to read ARGV");
388
+ tracing::warn!("Failed to read sys.platform");
347
389
 
348
390
  "<UNKNOWN>".into()
349
391
  });
350
392
 
351
- // Store the args, since threads will need it as part of the registration message.
352
- ARGV.set(args_prefix.clone())
353
- .expect("ARGV is only set in `trace_initialization`");
354
-
355
- args_prefix
393
+ function_trace::TraceInitialization {
394
+ program_name: ARGV
395
+ .get()
396
+ .expect("ARGV is set earlier in trace_initialization")
397
+ .clone(),
398
+ program_version: format!("py-functiontrace {}", env!("PACKAGE_VERSION")),
399
+ lang_version: python_version,
400
+ platform,
401
+ time: trace_time(),
402
+ }
356
403
  };
357
404
 
358
- let python_version = Python::with_gil(|py| {
359
- let version = py.version_info();
360
-
361
- format!(
362
- "Python {}.{}.{}",
363
- version.major, version.minor, version.patch
364
- )
365
- });
366
-
367
- let platform = Python::with_gil(|py| -> Result<String> {
368
- let platform = py
369
- .import("sys")
370
- .and_then(|sys| sys.getattr("platform"))
371
- .wrap_err("Failed to access sys.platform")?;
372
-
373
- Ok(platform
374
- .downcast::<pyo3::types::PyString>()
375
- .map_err(|e| eyre!("Failed to read sys.platform as string: {}", e))?
376
- .to_str()
377
- .wrap_err("Failed to read Rust string out of PyString")?
378
- .into())
379
- })
380
- .unwrap_or_else(|_err| {
381
- tracing::warn!("Failed to read sys.platform");
382
-
383
- "<UNKNOWN>".into()
384
- });
385
-
386
- function_trace::TraceInitialization {
387
- program_name: argv,
388
- program_version: format!("py-functiontrace {}", env!("PACKAGE_VERSION")),
389
- lang_version: python_version,
390
- platform,
391
- time: trace_time(),
392
- }
393
- };
394
-
395
- let mut socket = message_initialize();
405
+ let mut socket = message_initialize();
396
406
 
397
- init.serialize(&mut rmp_serde::encode::Serializer::new(&mut socket))
398
- .wrap_err("Failed to emit TraceInitialization message")?;
407
+ init.serialize(&mut rmp_serde::encode::Serializer::new(&mut socket))
408
+ .wrap_err("Failed to emit TraceInitialization message")?;
399
409
 
400
- // Close the initialization socket to trigger `functiontrace-server` to
401
- // read our message, since otherwise it'll wait forever for more data.
402
- socket
403
- .shutdown(std::net::Shutdown::Both)
404
- .wrap_err("Failed to close initialization socket")?;
410
+ // Close the initialization socket to trigger `functiontrace-server` to
411
+ // read our message, since otherwise it'll wait forever for more data.
412
+ socket
413
+ .shutdown(std::net::Shutdown::Both)
414
+ .wrap_err("Failed to close initialization socket")?;
415
+ }
405
416
 
406
417
  ////////////////////////////////////////////////////////////////////////////
407
418
  // Teardown Configuration
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes