Skip to main content

Python module for tracing Windows DLL loads

Project description

dlltracer

The dlltracer tool is an assistive tool for diagnosing import errors in CPython when they are caused by DLL resolution failures on Windows.

In general, any DLL load error is reported as an ImportError of the top-level extension module. No more specific information is available for CPython to display, which can make it difficult to diagnose.

This tool uses not-quite-documented performance events to report on the intermediate steps of importing an extension module. These events are undocumented and unsupported, so the format has been inferred by example and may change, but until it does it will report on the loads that actually occur. However, because it can't report on loads that never occur, you'll still need to do some work to diagnose the root cause of the failure.

The most useful static analysis tool is dumpbin, which is included with Visual Studio. When passed a DLL or PYD file and the /imports option, it will list all dependencies that should be loaded. It shows them by name, that is, before path resolution occurs.

dlltracer performs dynamic analysis, which shows the DLLs that are loaded at runtime with their full paths. Combined with understanding the dependency graph of your module, it is easier to diagnose why the overall import fails.

Install

pip install dlltracer

Where the pip command may be replaced by a more appropriate command for your environment, such as python -m pip or pip3.9.

Use

Note: Regardless of how output is collected, this tool must be run as Administrator. Otherwise, starting a trace will fail with a PermissionError. Only one thread may be tracing across your entire machine. Because the state of traces is not well managed by Windows, this tool will attempt to stop any other running traces.

A basic trace that prints messages to standard output is:

import dlltracer
import sys

with dlltracer.Trace(out=sys.stdout):
    import module_to_trace

The output may look like this (for import ssl):

LoadLibrary \Device\HarddiskVolume3\Windows\System32\kernel.appcore.dll
LoadLibrary \Device\HarddiskVolume3\Program Files\Python39\DLLs\_ssl.pyd
LoadLibrary \Device\HarddiskVolume3\Windows\System32\crypt32.dll
LoadLibrary \Device\HarddiskVolume3\Program Files\Python39\DLLs\libcrypto-1_1.dll
LoadLibrary \Device\HarddiskVolume3\Program Files\Python39\DLLs\libssl-1_1.dll
LoadLibrary \Device\HarddiskVolume3\Windows\System32\user32.dll
LoadLibrary \Device\HarddiskVolume3\Windows\System32\win32u.dll
LoadLibrary \Device\HarddiskVolume3\Windows\System32\gdi32.dll
LoadLibrary \Device\HarddiskVolume3\Windows\System32\gdi32full.dll
LoadLibrary \Device\HarddiskVolume3\Windows\System32\msvcp_win.dll
LoadLibrary \Device\HarddiskVolume3\Windows\System32\imm32.dll
LoadLibrary \Device\HarddiskVolume3\Program Files\Python39\DLLs\_socket.pyd
LoadLibrary \Device\HarddiskVolume3\Program Files\Python39\DLLs\select.pyd

A failed import may look like this (for import ssl but with libcrypto-1_1.dll missing):

LoadLibrary \Device\HarddiskVolume3\Windows\System32\kernel.appcore.dll
LoadLibrary \Device\HarddiskVolume3\Program Files\Python39\DLLs\_ssl.pyd
LoadLibrary \Device\HarddiskVolume3\Windows\System32\crypt32.dll
LoadLibrary \Device\HarddiskVolume3\Program Files\Python39\DLLs\libssl-1_1.dll
Failed \Device\HarddiskVolume3\Windows\System32\crypt32.dll
Failed \Device\HarddiskVolume3\Program Files\Python39\DLLs\libssl-1_1.dll
Failed \Device\HarddiskVolume3\Program Files\Python39\DLLs\_ssl.pyd
Traceback (most recent call last):
  File "C:\Projects\test-script.py", line 28, in <module>
    import ssl
  File "C:\Program Files\Python39\lib\ssl.py", line 98, in <module>
    import _ssl             # if we can't import it, let the error propagate
ImportError: DLL load failed while importing _ssl: The specified module could not be found.

Notice that the missing DLL is never mentioned, and so human analysis is necessary to diagnose the root cause.

Write to file

To write output to a file-like object (anything that can be passed to the file= argument of print), pass it as the out= argument of Trace.

import dlltracer

with open("log.txt", "w") as log:
    with dlltracer.Trace(out=log):
        import module_to_trace

Collect to list

To collect events to an iterable object, pass collect=True to Trace and bind the context manager. The result will be a list containing event objects, typically dlltracer.LoadEvent and dlltracer.LoadFailedEvent.

import dlltracer

with dlltracer.Trace(collect=True) as events:
    try:
        import module_to_trace
    except ImportError:
        # If we don't handle the error, program will exit before
        # we get to inspect the events.
        pass

# Inspect the events after ending the trace
all_loaded = {e.path for e in events if isinstance(e, dlltracer.LoadEvent)}
all_failed = {e.path for e in events if isinstance(e, dlltracer.LoadFailedEvent)}

Raise audit events

To raise audit events for DLL loads, pass audit=True to Trace. The events raised are dlltracer.load and dlltracer.failed, and both only include the path as an argument.

import dlltracer
import sys

def hook(event, args):
    if event == "dlltracer.load":
        # args = (path,)
        print("Loaded", args[0])
    elif event == "dlltracer.failed":
        # args = (path,)
        print("Failed", args[0])

sys.add_audit_hook(hook)

with dlltracer.Trace(audit=True):
    import module_to_trace

Additional events

Note: This is mainly intended for development of dlltracer.

Because event formats may change, and additional events may be of interest but are not yet handled, passing the debug=True option to Trace enables all events to be collected, written, or audited. Regular events are suppressed.

import dlltracer
import sys

def hook(event, args):
    if event != "dlltracer.debug":
        return

    # args schema:
    #   provider is a UUID representing the event source
    #   opcode is an int representing the operation
    #   header is bytes taken directly from the event header
    #   data is bytes taken directly from the event data
    provider, opcode, header, data = args

sys.add_audit_hook(hook)

with dlltracer.Trace(debug=True, audit=True, collect=True, out=sys.stderr) as events:
    try:
        import module_to_trace
    except ImportError:
        pass

for e in events:
    assert isinstance(e, dlltracer.DebugEvent)
    # DebugEvent contains provider, opcode, header and data as for the audit event

Contribute

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.

When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.

Trademarks

This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dlltracer-1.0.3.tar.gz (113.0 kB view details)

Uploaded Source

Built Distributions

dlltracer-1.0.3-cp313-cp313-win_amd64.whl (72.9 kB view details)

Uploaded CPython 3.13 Windows x86-64

dlltracer-1.0.3-cp312-cp312-win_amd64.whl (72.8 kB view details)

Uploaded CPython 3.12 Windows x86-64

dlltracer-1.0.3-cp311-cp311-win_amd64.whl (72.6 kB view details)

Uploaded CPython 3.11 Windows x86-64

dlltracer-1.0.3-cp310-cp310-win_amd64.whl (76.3 kB view details)

Uploaded CPython 3.10 Windows x86-64

dlltracer-1.0.3-cp39-cp39-win_amd64.whl (76.5 kB view details)

Uploaded CPython 3.9 Windows x86-64

dlltracer-1.0.3-cp38-cp38-win_amd64.whl (77.0 kB view details)

Uploaded CPython 3.8 Windows x86-64

File details

Details for the file dlltracer-1.0.3.tar.gz.

File metadata

  • Download URL: dlltracer-1.0.3.tar.gz
  • Upload date:
  • Size: 113.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: RestSharp/106.13.0.0

File hashes

Hashes for dlltracer-1.0.3.tar.gz
Algorithm Hash digest
SHA256 4c31d5d0cf621caf4f3e2534b1dd9f2649ee10f39547be2c18f400698d3d31c7
MD5 60e98e72f7cf6f647d5a87b0ef7fd508
BLAKE2b-256 63eac37339d7b98251b7209bd74ed819ccf5452ffbb13062ce44bc4789943c97

See more details on using hashes here.

File details

Details for the file dlltracer-1.0.3-cp313-cp313-win_amd64.whl.

File metadata

File hashes

Hashes for dlltracer-1.0.3-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 4612a89919dffd5a4e8afb4a344cf9dab93371a205a0cff33a4596e78ea1a680
MD5 5f8ffb38937486983f87b98df6d43623
BLAKE2b-256 153d3486172a44112fca704471005b09d1326b59e7bf24a4f1d917e4b2357e52

See more details on using hashes here.

File details

Details for the file dlltracer-1.0.3-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for dlltracer-1.0.3-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 6fecef51591daf8d10a3a0197c9812febd5fba4a192e2afe126642e24f5434ec
MD5 a296b17bc8cf42e2f58c2eafd1ddcf0c
BLAKE2b-256 334af811e93c6928b408730d115678b763eca4c6c34b1bac2fad83abd3485d53

See more details on using hashes here.

File details

Details for the file dlltracer-1.0.3-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for dlltracer-1.0.3-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 26e4f231ae924f1cd59128f4d08b56155a75bb5b0d61710cc5153b3c5f2b2dbe
MD5 7d13744db1d50e8742f22d0dc229f459
BLAKE2b-256 54bb84420a4b5df742b7e627b7427d2e0a12ee291af0564d5b2075f05c7241e0

See more details on using hashes here.

File details

Details for the file dlltracer-1.0.3-cp310-cp310-win_amd64.whl.

File metadata

File hashes

Hashes for dlltracer-1.0.3-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 bae321f153f9e8bec6b00b1bf5a51deacd82ad7dae957357c7231672d85660bf
MD5 d0dc3377370478d5af5f5db0f10662ee
BLAKE2b-256 19aa88340782217cf2197266b918d0080d60427e078d44419762687baa8b37bc

See more details on using hashes here.

File details

Details for the file dlltracer-1.0.3-cp39-cp39-win_amd64.whl.

File metadata

File hashes

Hashes for dlltracer-1.0.3-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 ded20fb470e07cdc343506c40018f683769114c040d0810d4d00190d6004d293
MD5 7c3e9f628fff6cd4644ee9dd44b04bd1
BLAKE2b-256 62b57dc9bf611173b4e74567443e12ffd14d1a941f02c75f1374d7be2b1e5ac7

See more details on using hashes here.

File details

Details for the file dlltracer-1.0.3-cp38-cp38-win_amd64.whl.

File metadata

File hashes

Hashes for dlltracer-1.0.3-cp38-cp38-win_amd64.whl
Algorithm Hash digest
SHA256 54cee25fd7ff7105765b467f9cea0cdd54757826fe009e61cc2223b51c430618
MD5 7cd5c57e30cd90a06fb8d30d035ea841
BLAKE2b-256 94b791e6ef8a9db5b44051670488896be7bc7aacfdda4a7476fdc48b25684de7

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page