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.
- {functiontrace-0.5.0/functiontrace.egg-info → functiontrace-0.5.1}/PKG-INFO +1 -1
- {functiontrace-0.5.0 → functiontrace-0.5.1}/build.rs +12 -2
- {functiontrace-0.5.0 → functiontrace-0.5.1/functiontrace.egg-info}/PKG-INFO +1 -1
- {functiontrace-0.5.0 → functiontrace-0.5.1}/functiontrace.py +2 -1
- {functiontrace-0.5.0 → functiontrace-0.5.1}/pyproject.toml +1 -1
- {functiontrace-0.5.0 → functiontrace-0.5.1}/rust/lib.rs +144 -133
- {functiontrace-0.5.0 → functiontrace-0.5.1}/Cargo.toml +0 -0
- {functiontrace-0.5.0 → functiontrace-0.5.1}/LICENSE.md +0 -0
- {functiontrace-0.5.0 → functiontrace-0.5.1}/MANIFEST.in +0 -0
- {functiontrace-0.5.0 → functiontrace-0.5.1}/README.rst +0 -0
- {functiontrace-0.5.0 → functiontrace-0.5.1}/functiontrace.egg-info/SOURCES.txt +0 -0
- {functiontrace-0.5.0 → functiontrace-0.5.1}/functiontrace.egg-info/dependency_links.txt +0 -0
- {functiontrace-0.5.0 → functiontrace-0.5.1}/functiontrace.egg-info/entry_points.txt +0 -0
- {functiontrace-0.5.0 → functiontrace-0.5.1}/functiontrace.egg-info/top_level.txt +0 -0
- {functiontrace-0.5.0 → functiontrace-0.5.1}/setup.cfg +0 -0
- {functiontrace-0.5.0 → functiontrace-0.5.1}/setup.py +0 -0
|
@@ -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
|
|
8
|
+
let project = pyproject
|
|
9
9
|
.get("project")
|
|
10
10
|
.and_then(|p| p.as_table())
|
|
11
|
-
.
|
|
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
|
|
|
@@ -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)
|
|
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:
|
|
@@ -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
|
-
|
|
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
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
|
388
|
+
tracing::warn!("Failed to read sys.platform");
|
|
347
389
|
|
|
348
390
|
"<UNKNOWN>".into()
|
|
349
391
|
});
|
|
350
392
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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
|
|
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
|
-
|
|
398
|
-
|
|
407
|
+
init.serialize(&mut rmp_serde::encode::Serializer::new(&mut socket))
|
|
408
|
+
.wrap_err("Failed to emit TraceInitialization message")?;
|
|
399
409
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|