oscura 0.0.1__py3-none-any.whl → 0.1.0__py3-none-any.whl
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.
- oscura/__init__.py +813 -8
- oscura/__main__.py +392 -0
- oscura/analyzers/__init__.py +37 -0
- oscura/analyzers/digital/__init__.py +177 -0
- oscura/analyzers/digital/bus.py +691 -0
- oscura/analyzers/digital/clock.py +805 -0
- oscura/analyzers/digital/correlation.py +720 -0
- oscura/analyzers/digital/edges.py +632 -0
- oscura/analyzers/digital/extraction.py +413 -0
- oscura/analyzers/digital/quality.py +878 -0
- oscura/analyzers/digital/signal_quality.py +877 -0
- oscura/analyzers/digital/thresholds.py +708 -0
- oscura/analyzers/digital/timing.py +1104 -0
- oscura/analyzers/eye/__init__.py +46 -0
- oscura/analyzers/eye/diagram.py +434 -0
- oscura/analyzers/eye/metrics.py +555 -0
- oscura/analyzers/jitter/__init__.py +83 -0
- oscura/analyzers/jitter/ber.py +333 -0
- oscura/analyzers/jitter/decomposition.py +759 -0
- oscura/analyzers/jitter/measurements.py +413 -0
- oscura/analyzers/jitter/spectrum.py +220 -0
- oscura/analyzers/measurements.py +40 -0
- oscura/analyzers/packet/__init__.py +171 -0
- oscura/analyzers/packet/daq.py +1077 -0
- oscura/analyzers/packet/metrics.py +437 -0
- oscura/analyzers/packet/parser.py +327 -0
- oscura/analyzers/packet/payload.py +2156 -0
- oscura/analyzers/packet/payload_analysis.py +1312 -0
- oscura/analyzers/packet/payload_extraction.py +236 -0
- oscura/analyzers/packet/payload_patterns.py +670 -0
- oscura/analyzers/packet/stream.py +359 -0
- oscura/analyzers/patterns/__init__.py +266 -0
- oscura/analyzers/patterns/clustering.py +1036 -0
- oscura/analyzers/patterns/discovery.py +539 -0
- oscura/analyzers/patterns/learning.py +797 -0
- oscura/analyzers/patterns/matching.py +1091 -0
- oscura/analyzers/patterns/periodic.py +650 -0
- oscura/analyzers/patterns/sequences.py +767 -0
- oscura/analyzers/power/__init__.py +116 -0
- oscura/analyzers/power/ac_power.py +391 -0
- oscura/analyzers/power/basic.py +383 -0
- oscura/analyzers/power/conduction.py +314 -0
- oscura/analyzers/power/efficiency.py +297 -0
- oscura/analyzers/power/ripple.py +356 -0
- oscura/analyzers/power/soa.py +372 -0
- oscura/analyzers/power/switching.py +479 -0
- oscura/analyzers/protocol/__init__.py +150 -0
- oscura/analyzers/protocols/__init__.py +150 -0
- oscura/analyzers/protocols/base.py +500 -0
- oscura/analyzers/protocols/can.py +620 -0
- oscura/analyzers/protocols/can_fd.py +448 -0
- oscura/analyzers/protocols/flexray.py +405 -0
- oscura/analyzers/protocols/hdlc.py +399 -0
- oscura/analyzers/protocols/i2c.py +368 -0
- oscura/analyzers/protocols/i2s.py +296 -0
- oscura/analyzers/protocols/jtag.py +393 -0
- oscura/analyzers/protocols/lin.py +445 -0
- oscura/analyzers/protocols/manchester.py +333 -0
- oscura/analyzers/protocols/onewire.py +501 -0
- oscura/analyzers/protocols/spi.py +334 -0
- oscura/analyzers/protocols/swd.py +325 -0
- oscura/analyzers/protocols/uart.py +393 -0
- oscura/analyzers/protocols/usb.py +495 -0
- oscura/analyzers/signal_integrity/__init__.py +63 -0
- oscura/analyzers/signal_integrity/embedding.py +294 -0
- oscura/analyzers/signal_integrity/equalization.py +370 -0
- oscura/analyzers/signal_integrity/sparams.py +484 -0
- oscura/analyzers/spectral/__init__.py +53 -0
- oscura/analyzers/spectral/chunked.py +273 -0
- oscura/analyzers/spectral/chunked_fft.py +571 -0
- oscura/analyzers/spectral/chunked_wavelet.py +391 -0
- oscura/analyzers/spectral/fft.py +92 -0
- oscura/analyzers/statistical/__init__.py +250 -0
- oscura/analyzers/statistical/checksum.py +923 -0
- oscura/analyzers/statistical/chunked_corr.py +228 -0
- oscura/analyzers/statistical/classification.py +778 -0
- oscura/analyzers/statistical/entropy.py +1113 -0
- oscura/analyzers/statistical/ngrams.py +614 -0
- oscura/analyzers/statistics/__init__.py +119 -0
- oscura/analyzers/statistics/advanced.py +885 -0
- oscura/analyzers/statistics/basic.py +263 -0
- oscura/analyzers/statistics/correlation.py +630 -0
- oscura/analyzers/statistics/distribution.py +298 -0
- oscura/analyzers/statistics/outliers.py +463 -0
- oscura/analyzers/statistics/streaming.py +93 -0
- oscura/analyzers/statistics/trend.py +520 -0
- oscura/analyzers/validation.py +598 -0
- oscura/analyzers/waveform/__init__.py +36 -0
- oscura/analyzers/waveform/measurements.py +943 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
- oscura/analyzers/waveform/spectral.py +1689 -0
- oscura/analyzers/waveform/wavelets.py +298 -0
- oscura/api/__init__.py +62 -0
- oscura/api/dsl.py +538 -0
- oscura/api/fluent.py +571 -0
- oscura/api/operators.py +498 -0
- oscura/api/optimization.py +392 -0
- oscura/api/profiling.py +396 -0
- oscura/automotive/__init__.py +73 -0
- oscura/automotive/can/__init__.py +52 -0
- oscura/automotive/can/analysis.py +356 -0
- oscura/automotive/can/checksum.py +250 -0
- oscura/automotive/can/correlation.py +212 -0
- oscura/automotive/can/discovery.py +355 -0
- oscura/automotive/can/message_wrapper.py +375 -0
- oscura/automotive/can/models.py +385 -0
- oscura/automotive/can/patterns.py +381 -0
- oscura/automotive/can/session.py +452 -0
- oscura/automotive/can/state_machine.py +300 -0
- oscura/automotive/can/stimulus_response.py +461 -0
- oscura/automotive/dbc/__init__.py +15 -0
- oscura/automotive/dbc/generator.py +156 -0
- oscura/automotive/dbc/parser.py +146 -0
- oscura/automotive/dtc/__init__.py +30 -0
- oscura/automotive/dtc/database.py +3036 -0
- oscura/automotive/j1939/__init__.py +14 -0
- oscura/automotive/j1939/decoder.py +745 -0
- oscura/automotive/loaders/__init__.py +35 -0
- oscura/automotive/loaders/asc.py +98 -0
- oscura/automotive/loaders/blf.py +77 -0
- oscura/automotive/loaders/csv_can.py +136 -0
- oscura/automotive/loaders/dispatcher.py +136 -0
- oscura/automotive/loaders/mdf.py +331 -0
- oscura/automotive/loaders/pcap.py +132 -0
- oscura/automotive/obd/__init__.py +14 -0
- oscura/automotive/obd/decoder.py +707 -0
- oscura/automotive/uds/__init__.py +48 -0
- oscura/automotive/uds/decoder.py +265 -0
- oscura/automotive/uds/models.py +64 -0
- oscura/automotive/visualization.py +369 -0
- oscura/batch/__init__.py +55 -0
- oscura/batch/advanced.py +627 -0
- oscura/batch/aggregate.py +300 -0
- oscura/batch/analyze.py +139 -0
- oscura/batch/logging.py +487 -0
- oscura/batch/metrics.py +556 -0
- oscura/builders/__init__.py +41 -0
- oscura/builders/signal_builder.py +1131 -0
- oscura/cli/__init__.py +14 -0
- oscura/cli/batch.py +339 -0
- oscura/cli/characterize.py +273 -0
- oscura/cli/compare.py +775 -0
- oscura/cli/decode.py +551 -0
- oscura/cli/main.py +247 -0
- oscura/cli/shell.py +350 -0
- oscura/comparison/__init__.py +66 -0
- oscura/comparison/compare.py +397 -0
- oscura/comparison/golden.py +487 -0
- oscura/comparison/limits.py +391 -0
- oscura/comparison/mask.py +434 -0
- oscura/comparison/trace_diff.py +30 -0
- oscura/comparison/visualization.py +481 -0
- oscura/compliance/__init__.py +70 -0
- oscura/compliance/advanced.py +756 -0
- oscura/compliance/masks.py +363 -0
- oscura/compliance/reporting.py +483 -0
- oscura/compliance/testing.py +298 -0
- oscura/component/__init__.py +38 -0
- oscura/component/impedance.py +365 -0
- oscura/component/reactive.py +598 -0
- oscura/component/transmission_line.py +312 -0
- oscura/config/__init__.py +191 -0
- oscura/config/defaults.py +254 -0
- oscura/config/loader.py +348 -0
- oscura/config/memory.py +271 -0
- oscura/config/migration.py +458 -0
- oscura/config/pipeline.py +1077 -0
- oscura/config/preferences.py +530 -0
- oscura/config/protocol.py +875 -0
- oscura/config/schema.py +713 -0
- oscura/config/settings.py +420 -0
- oscura/config/thresholds.py +599 -0
- oscura/convenience.py +457 -0
- oscura/core/__init__.py +299 -0
- oscura/core/audit.py +457 -0
- oscura/core/backend_selector.py +405 -0
- oscura/core/cache.py +590 -0
- oscura/core/cancellation.py +439 -0
- oscura/core/confidence.py +225 -0
- oscura/core/config.py +506 -0
- oscura/core/correlation.py +216 -0
- oscura/core/cross_domain.py +422 -0
- oscura/core/debug.py +301 -0
- oscura/core/edge_cases.py +541 -0
- oscura/core/exceptions.py +535 -0
- oscura/core/gpu_backend.py +523 -0
- oscura/core/lazy.py +832 -0
- oscura/core/log_query.py +540 -0
- oscura/core/logging.py +931 -0
- oscura/core/logging_advanced.py +952 -0
- oscura/core/memoize.py +171 -0
- oscura/core/memory_check.py +274 -0
- oscura/core/memory_guard.py +290 -0
- oscura/core/memory_limits.py +336 -0
- oscura/core/memory_monitor.py +453 -0
- oscura/core/memory_progress.py +465 -0
- oscura/core/memory_warnings.py +315 -0
- oscura/core/numba_backend.py +362 -0
- oscura/core/performance.py +352 -0
- oscura/core/progress.py +524 -0
- oscura/core/provenance.py +358 -0
- oscura/core/results.py +331 -0
- oscura/core/types.py +504 -0
- oscura/core/uncertainty.py +383 -0
- oscura/discovery/__init__.py +52 -0
- oscura/discovery/anomaly_detector.py +672 -0
- oscura/discovery/auto_decoder.py +415 -0
- oscura/discovery/comparison.py +497 -0
- oscura/discovery/quality_validator.py +528 -0
- oscura/discovery/signal_detector.py +769 -0
- oscura/dsl/__init__.py +73 -0
- oscura/dsl/commands.py +246 -0
- oscura/dsl/interpreter.py +455 -0
- oscura/dsl/parser.py +689 -0
- oscura/dsl/repl.py +172 -0
- oscura/exceptions.py +59 -0
- oscura/exploratory/__init__.py +111 -0
- oscura/exploratory/error_recovery.py +642 -0
- oscura/exploratory/fuzzy.py +513 -0
- oscura/exploratory/fuzzy_advanced.py +786 -0
- oscura/exploratory/legacy.py +831 -0
- oscura/exploratory/parse.py +358 -0
- oscura/exploratory/recovery.py +275 -0
- oscura/exploratory/sync.py +382 -0
- oscura/exploratory/unknown.py +707 -0
- oscura/export/__init__.py +25 -0
- oscura/export/wireshark/README.md +265 -0
- oscura/export/wireshark/__init__.py +47 -0
- oscura/export/wireshark/generator.py +312 -0
- oscura/export/wireshark/lua_builder.py +159 -0
- oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
- oscura/export/wireshark/type_mapping.py +165 -0
- oscura/export/wireshark/validator.py +105 -0
- oscura/exporters/__init__.py +94 -0
- oscura/exporters/csv.py +303 -0
- oscura/exporters/exporters.py +44 -0
- oscura/exporters/hdf5.py +219 -0
- oscura/exporters/html_export.py +701 -0
- oscura/exporters/json_export.py +291 -0
- oscura/exporters/markdown_export.py +367 -0
- oscura/exporters/matlab_export.py +354 -0
- oscura/exporters/npz_export.py +219 -0
- oscura/exporters/spice_export.py +210 -0
- oscura/extensibility/__init__.py +131 -0
- oscura/extensibility/docs.py +752 -0
- oscura/extensibility/extensions.py +1125 -0
- oscura/extensibility/logging.py +259 -0
- oscura/extensibility/measurements.py +485 -0
- oscura/extensibility/plugins.py +414 -0
- oscura/extensibility/registry.py +346 -0
- oscura/extensibility/templates.py +913 -0
- oscura/extensibility/validation.py +651 -0
- oscura/filtering/__init__.py +89 -0
- oscura/filtering/base.py +563 -0
- oscura/filtering/convenience.py +564 -0
- oscura/filtering/design.py +725 -0
- oscura/filtering/filters.py +32 -0
- oscura/filtering/introspection.py +605 -0
- oscura/guidance/__init__.py +24 -0
- oscura/guidance/recommender.py +429 -0
- oscura/guidance/wizard.py +518 -0
- oscura/inference/__init__.py +251 -0
- oscura/inference/active_learning/README.md +153 -0
- oscura/inference/active_learning/__init__.py +38 -0
- oscura/inference/active_learning/lstar.py +257 -0
- oscura/inference/active_learning/observation_table.py +230 -0
- oscura/inference/active_learning/oracle.py +78 -0
- oscura/inference/active_learning/teachers/__init__.py +15 -0
- oscura/inference/active_learning/teachers/simulator.py +192 -0
- oscura/inference/adaptive_tuning.py +453 -0
- oscura/inference/alignment.py +653 -0
- oscura/inference/bayesian.py +943 -0
- oscura/inference/binary.py +1016 -0
- oscura/inference/crc_reverse.py +711 -0
- oscura/inference/logic.py +288 -0
- oscura/inference/message_format.py +1305 -0
- oscura/inference/protocol.py +417 -0
- oscura/inference/protocol_dsl.py +1084 -0
- oscura/inference/protocol_library.py +1230 -0
- oscura/inference/sequences.py +809 -0
- oscura/inference/signal_intelligence.py +1509 -0
- oscura/inference/spectral.py +215 -0
- oscura/inference/state_machine.py +634 -0
- oscura/inference/stream.py +918 -0
- oscura/integrations/__init__.py +59 -0
- oscura/integrations/llm.py +1827 -0
- oscura/jupyter/__init__.py +32 -0
- oscura/jupyter/display.py +268 -0
- oscura/jupyter/magic.py +334 -0
- oscura/loaders/__init__.py +526 -0
- oscura/loaders/binary.py +69 -0
- oscura/loaders/configurable.py +1255 -0
- oscura/loaders/csv.py +26 -0
- oscura/loaders/csv_loader.py +473 -0
- oscura/loaders/hdf5.py +9 -0
- oscura/loaders/hdf5_loader.py +510 -0
- oscura/loaders/lazy.py +370 -0
- oscura/loaders/mmap_loader.py +583 -0
- oscura/loaders/numpy_loader.py +436 -0
- oscura/loaders/pcap.py +432 -0
- oscura/loaders/preprocessing.py +368 -0
- oscura/loaders/rigol.py +287 -0
- oscura/loaders/sigrok.py +321 -0
- oscura/loaders/tdms.py +367 -0
- oscura/loaders/tektronix.py +711 -0
- oscura/loaders/validation.py +584 -0
- oscura/loaders/vcd.py +464 -0
- oscura/loaders/wav.py +233 -0
- oscura/math/__init__.py +45 -0
- oscura/math/arithmetic.py +824 -0
- oscura/math/interpolation.py +413 -0
- oscura/onboarding/__init__.py +39 -0
- oscura/onboarding/help.py +498 -0
- oscura/onboarding/tutorials.py +405 -0
- oscura/onboarding/wizard.py +466 -0
- oscura/optimization/__init__.py +19 -0
- oscura/optimization/parallel.py +440 -0
- oscura/optimization/search.py +532 -0
- oscura/pipeline/__init__.py +43 -0
- oscura/pipeline/base.py +338 -0
- oscura/pipeline/composition.py +242 -0
- oscura/pipeline/parallel.py +448 -0
- oscura/pipeline/pipeline.py +375 -0
- oscura/pipeline/reverse_engineering.py +1119 -0
- oscura/plugins/__init__.py +122 -0
- oscura/plugins/base.py +272 -0
- oscura/plugins/cli.py +497 -0
- oscura/plugins/discovery.py +411 -0
- oscura/plugins/isolation.py +418 -0
- oscura/plugins/lifecycle.py +959 -0
- oscura/plugins/manager.py +493 -0
- oscura/plugins/registry.py +421 -0
- oscura/plugins/versioning.py +372 -0
- oscura/py.typed +0 -0
- oscura/quality/__init__.py +65 -0
- oscura/quality/ensemble.py +740 -0
- oscura/quality/explainer.py +338 -0
- oscura/quality/scoring.py +616 -0
- oscura/quality/warnings.py +456 -0
- oscura/reporting/__init__.py +248 -0
- oscura/reporting/advanced.py +1234 -0
- oscura/reporting/analyze.py +448 -0
- oscura/reporting/argument_preparer.py +596 -0
- oscura/reporting/auto_report.py +507 -0
- oscura/reporting/batch.py +615 -0
- oscura/reporting/chart_selection.py +223 -0
- oscura/reporting/comparison.py +330 -0
- oscura/reporting/config.py +615 -0
- oscura/reporting/content/__init__.py +39 -0
- oscura/reporting/content/executive.py +127 -0
- oscura/reporting/content/filtering.py +191 -0
- oscura/reporting/content/minimal.py +257 -0
- oscura/reporting/content/verbosity.py +162 -0
- oscura/reporting/core.py +508 -0
- oscura/reporting/core_formats/__init__.py +17 -0
- oscura/reporting/core_formats/multi_format.py +210 -0
- oscura/reporting/engine.py +836 -0
- oscura/reporting/export.py +366 -0
- oscura/reporting/formatting/__init__.py +129 -0
- oscura/reporting/formatting/emphasis.py +81 -0
- oscura/reporting/formatting/numbers.py +403 -0
- oscura/reporting/formatting/standards.py +55 -0
- oscura/reporting/formatting.py +466 -0
- oscura/reporting/html.py +578 -0
- oscura/reporting/index.py +590 -0
- oscura/reporting/multichannel.py +296 -0
- oscura/reporting/output.py +379 -0
- oscura/reporting/pdf.py +373 -0
- oscura/reporting/plots.py +731 -0
- oscura/reporting/pptx_export.py +360 -0
- oscura/reporting/renderers/__init__.py +11 -0
- oscura/reporting/renderers/pdf.py +94 -0
- oscura/reporting/sections.py +471 -0
- oscura/reporting/standards.py +680 -0
- oscura/reporting/summary_generator.py +368 -0
- oscura/reporting/tables.py +397 -0
- oscura/reporting/template_system.py +724 -0
- oscura/reporting/templates/__init__.py +15 -0
- oscura/reporting/templates/definition.py +205 -0
- oscura/reporting/templates/index.html +649 -0
- oscura/reporting/templates/index.md +173 -0
- oscura/schemas/__init__.py +158 -0
- oscura/schemas/bus_configuration.json +322 -0
- oscura/schemas/device_mapping.json +182 -0
- oscura/schemas/packet_format.json +418 -0
- oscura/schemas/protocol_definition.json +363 -0
- oscura/search/__init__.py +16 -0
- oscura/search/anomaly.py +292 -0
- oscura/search/context.py +149 -0
- oscura/search/pattern.py +160 -0
- oscura/session/__init__.py +34 -0
- oscura/session/annotations.py +289 -0
- oscura/session/history.py +313 -0
- oscura/session/session.py +445 -0
- oscura/streaming/__init__.py +43 -0
- oscura/streaming/chunked.py +611 -0
- oscura/streaming/progressive.py +393 -0
- oscura/streaming/realtime.py +622 -0
- oscura/testing/__init__.py +54 -0
- oscura/testing/synthetic.py +808 -0
- oscura/triggering/__init__.py +68 -0
- oscura/triggering/base.py +229 -0
- oscura/triggering/edge.py +353 -0
- oscura/triggering/pattern.py +344 -0
- oscura/triggering/pulse.py +581 -0
- oscura/triggering/window.py +453 -0
- oscura/ui/__init__.py +48 -0
- oscura/ui/formatters.py +526 -0
- oscura/ui/progressive_display.py +340 -0
- oscura/utils/__init__.py +99 -0
- oscura/utils/autodetect.py +338 -0
- oscura/utils/buffer.py +389 -0
- oscura/utils/lazy.py +407 -0
- oscura/utils/lazy_imports.py +147 -0
- oscura/utils/memory.py +836 -0
- oscura/utils/memory_advanced.py +1326 -0
- oscura/utils/memory_extensions.py +465 -0
- oscura/utils/progressive.py +352 -0
- oscura/utils/windowing.py +362 -0
- oscura/visualization/__init__.py +321 -0
- oscura/visualization/accessibility.py +526 -0
- oscura/visualization/annotations.py +374 -0
- oscura/visualization/axis_scaling.py +305 -0
- oscura/visualization/colors.py +453 -0
- oscura/visualization/digital.py +337 -0
- oscura/visualization/eye.py +420 -0
- oscura/visualization/histogram.py +281 -0
- oscura/visualization/interactive.py +858 -0
- oscura/visualization/jitter.py +702 -0
- oscura/visualization/keyboard.py +394 -0
- oscura/visualization/layout.py +365 -0
- oscura/visualization/optimization.py +1028 -0
- oscura/visualization/palettes.py +446 -0
- oscura/visualization/plot.py +92 -0
- oscura/visualization/power.py +290 -0
- oscura/visualization/power_extended.py +626 -0
- oscura/visualization/presets.py +467 -0
- oscura/visualization/protocols.py +932 -0
- oscura/visualization/render.py +207 -0
- oscura/visualization/rendering.py +444 -0
- oscura/visualization/reverse_engineering.py +791 -0
- oscura/visualization/signal_integrity.py +808 -0
- oscura/visualization/specialized.py +553 -0
- oscura/visualization/spectral.py +811 -0
- oscura/visualization/styles.py +381 -0
- oscura/visualization/thumbnails.py +311 -0
- oscura/visualization/time_axis.py +351 -0
- oscura/visualization/waveform.py +367 -0
- oscura/workflow/__init__.py +13 -0
- oscura/workflow/dag.py +377 -0
- oscura/workflows/__init__.py +58 -0
- oscura/workflows/compliance.py +280 -0
- oscura/workflows/digital.py +272 -0
- oscura/workflows/multi_trace.py +502 -0
- oscura/workflows/power.py +178 -0
- oscura/workflows/protocol.py +492 -0
- oscura/workflows/reverse_engineering.py +639 -0
- oscura/workflows/signal_integrity.py +227 -0
- oscura-0.1.0.dist-info/METADATA +300 -0
- oscura-0.1.0.dist-info/RECORD +463 -0
- oscura-0.1.0.dist-info/entry_points.txt +2 -0
- {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/licenses/LICENSE +1 -1
- oscura-0.0.1.dist-info/METADATA +0 -63
- oscura-0.0.1.dist-info/RECORD +0 -5
- {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
"""Memory usage logging and progress tracking for TraceKit operations.
|
|
2
|
+
|
|
3
|
+
This module provides detailed memory profiling logs for debugging and monitoring
|
|
4
|
+
memory usage during long-running operations.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> from oscura.core.memory_progress import MemoryLogger
|
|
9
|
+
>>> logger = MemoryLogger("analysis.log", format="csv")
|
|
10
|
+
>>> with logger:
|
|
11
|
+
... for i in range(1000):
|
|
12
|
+
... # Perform work
|
|
13
|
+
... logger.log_operation("fft", iteration=i)
|
|
14
|
+
>>> stats = logger.get_summary()
|
|
15
|
+
|
|
16
|
+
References:
|
|
17
|
+
psutil documentation for memory monitoring
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import csv
|
|
23
|
+
import json
|
|
24
|
+
import os
|
|
25
|
+
import time
|
|
26
|
+
from contextlib import contextmanager
|
|
27
|
+
from dataclasses import asdict, dataclass
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import TYPE_CHECKING, Any, Literal, TextIO
|
|
30
|
+
|
|
31
|
+
from oscura.utils.memory import get_available_memory, get_memory_pressure
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from collections.abc import Iterator
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class MemoryLogEntry:
|
|
39
|
+
"""Single memory log entry.
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
Attributes:
|
|
43
|
+
timestamp: Time of log entry (seconds since epoch).
|
|
44
|
+
operation: Name of operation being performed.
|
|
45
|
+
iteration: Iteration number (if applicable).
|
|
46
|
+
memory_used: Process memory usage (bytes).
|
|
47
|
+
memory_peak: Peak memory since start (bytes).
|
|
48
|
+
memory_available: System available memory (bytes).
|
|
49
|
+
memory_pressure: Memory pressure (0.0-1.0).
|
|
50
|
+
eta_seconds: Estimated time to completion (seconds).
|
|
51
|
+
message: Optional descriptive message.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
timestamp: float
|
|
55
|
+
operation: str
|
|
56
|
+
iteration: int | None
|
|
57
|
+
memory_used: int
|
|
58
|
+
memory_peak: int
|
|
59
|
+
memory_available: int
|
|
60
|
+
memory_pressure: float
|
|
61
|
+
eta_seconds: float
|
|
62
|
+
message: str
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class MemoryLogger:
|
|
66
|
+
"""Logger for detailed memory profiling during operations.
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
Logs memory usage at each operation with timestamps and metadata.
|
|
70
|
+
Supports CSV and JSON output formats.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
log_file: Path to output log file.
|
|
74
|
+
format: Output format ('csv' or 'json').
|
|
75
|
+
auto_flush: Flush after each write (default: True).
|
|
76
|
+
enable_console: Also print to console (default: False).
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
>>> logger = MemoryLogger("memory.csv", format="csv")
|
|
80
|
+
>>> with logger:
|
|
81
|
+
... for i in range(100):
|
|
82
|
+
... # Do work
|
|
83
|
+
... logger.log_operation("processing", iteration=i)
|
|
84
|
+
>>> print(logger.get_summary())
|
|
85
|
+
|
|
86
|
+
References:
|
|
87
|
+
MEM-025: Memory Usage Logging
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
def __init__(
|
|
91
|
+
self,
|
|
92
|
+
log_file: str | Path,
|
|
93
|
+
*,
|
|
94
|
+
format: Literal["csv", "json"] = "csv",
|
|
95
|
+
auto_flush: bool = True,
|
|
96
|
+
enable_console: bool = False,
|
|
97
|
+
):
|
|
98
|
+
"""Initialize memory logger.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
log_file: Path to log file.
|
|
102
|
+
format: Output format ('csv' or 'json').
|
|
103
|
+
auto_flush: Flush after each entry.
|
|
104
|
+
enable_console: Print to console as well.
|
|
105
|
+
"""
|
|
106
|
+
self.log_file = Path(log_file)
|
|
107
|
+
self.format = format
|
|
108
|
+
self.auto_flush = auto_flush
|
|
109
|
+
self.enable_console = enable_console
|
|
110
|
+
|
|
111
|
+
# State
|
|
112
|
+
self._entries: list[MemoryLogEntry] = []
|
|
113
|
+
self._file_handle: TextIO | None = None
|
|
114
|
+
self._csv_writer: Any = None
|
|
115
|
+
self._start_time = 0.0
|
|
116
|
+
self._start_memory = 0
|
|
117
|
+
self._peak_memory = 0
|
|
118
|
+
|
|
119
|
+
# Create directory if needed
|
|
120
|
+
self.log_file.parent.mkdir(parents=True, exist_ok=True)
|
|
121
|
+
|
|
122
|
+
def __enter__(self) -> MemoryLogger:
|
|
123
|
+
"""Enter context and initialize logging."""
|
|
124
|
+
self._start_time = time.time()
|
|
125
|
+
self._start_memory = self._get_process_memory()
|
|
126
|
+
self._peak_memory = self._start_memory
|
|
127
|
+
|
|
128
|
+
# Open log file
|
|
129
|
+
self._file_handle = open(self.log_file, "w", newline="")
|
|
130
|
+
|
|
131
|
+
# Initialize CSV writer if needed
|
|
132
|
+
if self.format == "csv":
|
|
133
|
+
self._csv_writer = csv.DictWriter(
|
|
134
|
+
self._file_handle, # type: ignore[arg-type]
|
|
135
|
+
fieldnames=[
|
|
136
|
+
"timestamp",
|
|
137
|
+
"operation",
|
|
138
|
+
"iteration",
|
|
139
|
+
"memory_used",
|
|
140
|
+
"memory_peak",
|
|
141
|
+
"memory_available",
|
|
142
|
+
"memory_pressure",
|
|
143
|
+
"eta_seconds",
|
|
144
|
+
"message",
|
|
145
|
+
],
|
|
146
|
+
)
|
|
147
|
+
self._csv_writer.writeheader() # type: ignore[attr-defined]
|
|
148
|
+
if self.auto_flush:
|
|
149
|
+
self._file_handle.flush() # type: ignore[attr-defined]
|
|
150
|
+
|
|
151
|
+
return self
|
|
152
|
+
|
|
153
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> None: # type: ignore[no-untyped-def]
|
|
154
|
+
"""Exit context and finalize logging."""
|
|
155
|
+
# Note: exc_val and exc_tb intentionally unused but required for Python 3.11+ compatibility
|
|
156
|
+
# Write summary for JSON format
|
|
157
|
+
if self.format == "json" and self._file_handle:
|
|
158
|
+
summary = { # type: ignore[unreachable]
|
|
159
|
+
"entries": [asdict(entry) for entry in self._entries],
|
|
160
|
+
"summary": self._get_summary_dict(),
|
|
161
|
+
}
|
|
162
|
+
json.dump(summary, self._file_handle, indent=2)
|
|
163
|
+
|
|
164
|
+
# Close file
|
|
165
|
+
if self._file_handle:
|
|
166
|
+
self._file_handle.close() # type: ignore[unreachable]
|
|
167
|
+
self._file_handle = None
|
|
168
|
+
|
|
169
|
+
def log_operation(
|
|
170
|
+
self,
|
|
171
|
+
operation: str,
|
|
172
|
+
*,
|
|
173
|
+
iteration: int | None = None,
|
|
174
|
+
eta_seconds: float = 0.0,
|
|
175
|
+
message: str = "",
|
|
176
|
+
) -> None:
|
|
177
|
+
"""Log memory usage for an operation.
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
operation: Name of operation.
|
|
182
|
+
iteration: Iteration number (optional).
|
|
183
|
+
eta_seconds: Estimated time to completion.
|
|
184
|
+
message: Optional descriptive message.
|
|
185
|
+
|
|
186
|
+
Example:
|
|
187
|
+
>>> logger.log_operation("fft", iteration=100, eta_seconds=5.2)
|
|
188
|
+
|
|
189
|
+
References:
|
|
190
|
+
MEM-025: Memory Usage Logging
|
|
191
|
+
"""
|
|
192
|
+
# Get memory metrics
|
|
193
|
+
memory_used = self._get_process_memory()
|
|
194
|
+
memory_available = get_available_memory()
|
|
195
|
+
memory_pressure = get_memory_pressure()
|
|
196
|
+
|
|
197
|
+
# Update peak
|
|
198
|
+
self._peak_memory = max(self._peak_memory, memory_used)
|
|
199
|
+
|
|
200
|
+
# Create entry
|
|
201
|
+
entry = MemoryLogEntry(
|
|
202
|
+
timestamp=time.time(),
|
|
203
|
+
operation=operation,
|
|
204
|
+
iteration=iteration,
|
|
205
|
+
memory_used=memory_used,
|
|
206
|
+
memory_peak=self._peak_memory,
|
|
207
|
+
memory_available=memory_available,
|
|
208
|
+
memory_pressure=memory_pressure,
|
|
209
|
+
eta_seconds=eta_seconds,
|
|
210
|
+
message=message,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
self._entries.append(entry)
|
|
214
|
+
|
|
215
|
+
# Write to file
|
|
216
|
+
if self._file_handle and self.format == "csv": # type: ignore[unreachable]
|
|
217
|
+
self._csv_writer.writerow(asdict(entry)) # type: ignore[unreachable]
|
|
218
|
+
if self.auto_flush:
|
|
219
|
+
self._file_handle.flush()
|
|
220
|
+
|
|
221
|
+
# Console output
|
|
222
|
+
if self.enable_console:
|
|
223
|
+
print(self._format_entry(entry))
|
|
224
|
+
|
|
225
|
+
def log_progress(
|
|
226
|
+
self,
|
|
227
|
+
operation: str,
|
|
228
|
+
current: int,
|
|
229
|
+
total: int,
|
|
230
|
+
*,
|
|
231
|
+
message: str = "",
|
|
232
|
+
) -> None:
|
|
233
|
+
"""Log memory usage with progress information.
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
Convenience method that calculates ETA from progress.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
operation: Name of operation.
|
|
240
|
+
current: Current progress value.
|
|
241
|
+
total: Total progress value.
|
|
242
|
+
message: Optional message.
|
|
243
|
+
|
|
244
|
+
Example:
|
|
245
|
+
>>> for i in range(1000):
|
|
246
|
+
... logger.log_progress("analysis", i + 1, 1000)
|
|
247
|
+
|
|
248
|
+
References:
|
|
249
|
+
MEM-024: Memory-Aware Progress Callback
|
|
250
|
+
MEM-025: Memory Usage Logging
|
|
251
|
+
"""
|
|
252
|
+
# Calculate ETA
|
|
253
|
+
elapsed = time.time() - self._start_time
|
|
254
|
+
eta = elapsed / current * (total - current) if current > 0 else 0.0
|
|
255
|
+
|
|
256
|
+
self.log_operation(
|
|
257
|
+
operation,
|
|
258
|
+
iteration=current,
|
|
259
|
+
eta_seconds=eta,
|
|
260
|
+
message=message,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
def get_summary(self) -> str:
|
|
264
|
+
"""Get human-readable summary of memory usage.
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
Formatted summary string.
|
|
268
|
+
|
|
269
|
+
Example:
|
|
270
|
+
>>> logger = MemoryLogger("test.log")
|
|
271
|
+
>>> with logger:
|
|
272
|
+
... logger.log_operation("test")
|
|
273
|
+
>>> print(logger.get_summary())
|
|
274
|
+
|
|
275
|
+
References:
|
|
276
|
+
MEM-025: Memory Usage Logging
|
|
277
|
+
"""
|
|
278
|
+
summary = self._get_summary_dict()
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
f"Memory Usage Summary:\n"
|
|
282
|
+
f" Entries: {summary['entry_count']}\n"
|
|
283
|
+
f" Duration: {summary['duration']:.2f}s\n"
|
|
284
|
+
f" Start Memory: {summary['start_memory'] / 1e9:.2f} GB\n"
|
|
285
|
+
f" Peak Memory: {summary['peak_memory'] / 1e9:.2f} GB\n"
|
|
286
|
+
f" Delta: {summary['memory_delta'] / 1e9:.2f} GB\n"
|
|
287
|
+
f" Min Available: {summary['min_available'] / 1e9:.2f} GB\n"
|
|
288
|
+
f" Max Pressure: {summary['max_pressure'] * 100:.1f}%\n"
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
def get_entries(self) -> list[MemoryLogEntry]:
|
|
292
|
+
"""Get all logged entries.
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
List of memory log entries.
|
|
296
|
+
|
|
297
|
+
References:
|
|
298
|
+
MEM-025: Memory Usage Logging
|
|
299
|
+
"""
|
|
300
|
+
return self._entries.copy()
|
|
301
|
+
|
|
302
|
+
def _get_summary_dict(self) -> dict: # type: ignore[type-arg]
|
|
303
|
+
"""Get summary statistics as dictionary."""
|
|
304
|
+
if not self._entries:
|
|
305
|
+
return {
|
|
306
|
+
"entry_count": 0,
|
|
307
|
+
"duration": 0.0,
|
|
308
|
+
"start_memory": self._start_memory,
|
|
309
|
+
"peak_memory": self._peak_memory,
|
|
310
|
+
"memory_delta": 0,
|
|
311
|
+
"min_available": 0,
|
|
312
|
+
"max_pressure": 0.0,
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
duration = self._entries[-1].timestamp - self._entries[0].timestamp
|
|
316
|
+
min_available = min(e.memory_available for e in self._entries)
|
|
317
|
+
max_pressure = max(e.memory_pressure for e in self._entries)
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
"entry_count": len(self._entries),
|
|
321
|
+
"duration": duration,
|
|
322
|
+
"start_memory": self._start_memory,
|
|
323
|
+
"peak_memory": self._peak_memory,
|
|
324
|
+
"memory_delta": self._peak_memory - self._start_memory,
|
|
325
|
+
"min_available": min_available,
|
|
326
|
+
"max_pressure": max_pressure,
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
def _format_entry(self, entry: MemoryLogEntry) -> str:
|
|
330
|
+
"""Format entry for console output."""
|
|
331
|
+
elapsed = entry.timestamp - self._start_time
|
|
332
|
+
return (
|
|
333
|
+
f"[{elapsed:7.2f}s] {entry.operation:20s} | "
|
|
334
|
+
f"Used: {entry.memory_used / 1e9:6.2f} GB | "
|
|
335
|
+
f"Peak: {entry.memory_peak / 1e9:6.2f} GB | "
|
|
336
|
+
f"Avail: {entry.memory_available / 1e9:6.2f} GB | "
|
|
337
|
+
f"Pressure: {entry.memory_pressure * 100:5.1f}%"
|
|
338
|
+
+ (f" | {entry.message}" if entry.message else "")
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
def _get_process_memory(self) -> int:
|
|
342
|
+
"""Get current process memory usage in bytes."""
|
|
343
|
+
try:
|
|
344
|
+
import psutil
|
|
345
|
+
|
|
346
|
+
process = psutil.Process()
|
|
347
|
+
return process.memory_info().rss # type: ignore[no-any-return]
|
|
348
|
+
except ImportError:
|
|
349
|
+
# Fallback: estimate from system memory
|
|
350
|
+
from oscura.utils.memory import get_total_memory
|
|
351
|
+
|
|
352
|
+
return get_total_memory() - get_available_memory()
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
@contextmanager
|
|
356
|
+
def log_memory(
|
|
357
|
+
log_file: str | Path,
|
|
358
|
+
*,
|
|
359
|
+
format: Literal["csv", "json"] = "csv",
|
|
360
|
+
enable_console: bool = False,
|
|
361
|
+
) -> Iterator[MemoryLogger]:
|
|
362
|
+
"""Context manager for memory logging.
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
Convenience function that wraps MemoryLogger.
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
log_file: Path to log file.
|
|
369
|
+
format: Output format ('csv' or 'json').
|
|
370
|
+
enable_console: Print to console.
|
|
371
|
+
|
|
372
|
+
Yields:
|
|
373
|
+
MemoryLogger instance.
|
|
374
|
+
|
|
375
|
+
Example:
|
|
376
|
+
>>> with log_memory("analysis.csv") as logger:
|
|
377
|
+
... for i in range(1000):
|
|
378
|
+
... # Do work
|
|
379
|
+
... logger.log_progress("processing", i + 1, 1000)
|
|
380
|
+
|
|
381
|
+
References:
|
|
382
|
+
MEM-025: Memory Usage Logging
|
|
383
|
+
"""
|
|
384
|
+
logger = MemoryLogger(log_file, format=format, enable_console=enable_console)
|
|
385
|
+
with logger:
|
|
386
|
+
yield logger
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def create_progress_callback_with_logging(
|
|
390
|
+
logger: MemoryLogger,
|
|
391
|
+
operation: str,
|
|
392
|
+
) -> callable: # type: ignore[valid-type]
|
|
393
|
+
"""Create progress callback that logs to MemoryLogger.
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
Returns a callback function compatible with progress tracking APIs
|
|
397
|
+
that automatically logs memory usage.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
logger: MemoryLogger instance to log to.
|
|
401
|
+
operation: Name of operation.
|
|
402
|
+
|
|
403
|
+
Returns:
|
|
404
|
+
Progress callback function.
|
|
405
|
+
|
|
406
|
+
Example:
|
|
407
|
+
>>> logger = MemoryLogger("test.log")
|
|
408
|
+
>>> callback = create_progress_callback_with_logging(logger, "fft")
|
|
409
|
+
>>> callback(50, 100, "Processing")
|
|
410
|
+
|
|
411
|
+
References:
|
|
412
|
+
MEM-024: Memory-Aware Progress Callback
|
|
413
|
+
MEM-025: Memory Usage Logging
|
|
414
|
+
"""
|
|
415
|
+
|
|
416
|
+
def callback(current: int, total: int, message: str) -> None:
|
|
417
|
+
"""Progress callback with memory logging."""
|
|
418
|
+
logger.log_progress(operation, current, total, message=message)
|
|
419
|
+
|
|
420
|
+
return callback
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def enable_memory_logging_from_cli(
|
|
424
|
+
log_file: str | Path | None = None,
|
|
425
|
+
) -> MemoryLogger | None:
|
|
426
|
+
"""Enable memory logging from CLI flag.
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
Checks for --log-memory CLI flag and returns logger if enabled.
|
|
430
|
+
|
|
431
|
+
Args:
|
|
432
|
+
log_file: Override log file path (default: auto-generate).
|
|
433
|
+
|
|
434
|
+
Returns:
|
|
435
|
+
MemoryLogger if enabled, None otherwise.
|
|
436
|
+
|
|
437
|
+
Example:
|
|
438
|
+
>>> logger = enable_memory_logging_from_cli()
|
|
439
|
+
>>> if logger:
|
|
440
|
+
... with logger:
|
|
441
|
+
... # Operations are logged
|
|
442
|
+
... logger.log_operation("test")
|
|
443
|
+
|
|
444
|
+
References:
|
|
445
|
+
MEM-025: Memory Usage Logging
|
|
446
|
+
"""
|
|
447
|
+
# Check environment variable
|
|
448
|
+
if os.environ.get("TK_LOG_MEMORY", "").lower() not in ("1", "true", "yes"):
|
|
449
|
+
return None
|
|
450
|
+
|
|
451
|
+
# Generate log file name if not provided
|
|
452
|
+
if log_file is None:
|
|
453
|
+
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
|
454
|
+
log_file = Path(f"oscura_memory_{timestamp}.csv")
|
|
455
|
+
|
|
456
|
+
return MemoryLogger(log_file, format="csv", enable_console=False)
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
__all__ = [
|
|
460
|
+
"MemoryLogEntry",
|
|
461
|
+
"MemoryLogger",
|
|
462
|
+
"create_progress_callback_with_logging",
|
|
463
|
+
"enable_memory_logging_from_cli",
|
|
464
|
+
"log_memory",
|
|
465
|
+
]
|