Skip to content

Commit 5ada735

Browse files
Merge pull request #159 from LedgerHQ/tdj/debug_over_usb
add debugApp script to capture app PRINTF output over USB CDC
2 parents 1fdec31 + 2b8d175 commit 5ada735

File tree

3 files changed

+158
-1
lines changed

3 files changed

+158
-1
lines changed

doc/source/script_reference.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ checkGenuine.py
1111
:func: get_argparser
1212
:prog: python -m ledgerblue.checkGenuine
1313

14+
.. _debugApp.py:
15+
16+
debugApp.py
17+
-----------
18+
19+
.. argparse::
20+
:module: ledgerblue.debugApp
21+
:func: get_argparser
22+
:prog: python -m ledgerblue.debugApp
23+
1424
.. _deleteApp.py:
1525

1626
deleteApp.py

ledgerblue/debugApp.py

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
"""
2+
*******************************************************************************
3+
* Ledger Blue
4+
* (c) 2016 Ledger
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
********************************************************************************
18+
"""
19+
20+
import argparse
21+
import sys
22+
import time
23+
from datetime import datetime
24+
25+
import serial
26+
import serial.tools.list_ports
27+
28+
LEDGER_VENDOR_ID = 0x2C97
29+
30+
31+
def get_argparser():
32+
parser = argparse.ArgumentParser(
33+
description="Listen to debug output from a Ledger app compiled with DEBUG_OVER_USB."
34+
)
35+
parser.add_argument(
36+
"--port", "-p",
37+
help="Serial port to use (e.g. COM3 on Windows, /dev/ttyACM0 on Linux). Auto-detected if omitted.",
38+
type=str,
39+
default=None,
40+
)
41+
parser.add_argument(
42+
"--baudrate", "-b",
43+
help="Baud rate for the serial connection (default: 115200).",
44+
type=int,
45+
default=115200,
46+
)
47+
parser.add_argument(
48+
"--output", "-o",
49+
help="Write debug output to a file in addition to stdout.",
50+
type=str,
51+
default=None,
52+
)
53+
return parser
54+
55+
56+
def find_ledger_cdc_port(port=None):
57+
"""Find the Ledger device's CDC (virtual serial) port.
58+
59+
When DEBUG_OVER_USB is enabled in a Ledger app, the device exposes
60+
an additional USB CDC interface that sends PRINTF debug output.
61+
"""
62+
if port:
63+
return port
64+
65+
for p in serial.tools.list_ports.comports():
66+
if p.vid == LEDGER_VENDOR_ID:
67+
return p.device
68+
69+
return None
70+
71+
72+
def wait_for_cdc_port(port=None, timeout=0):
73+
"""Wait for the Ledger CDC port to appear.
74+
75+
If timeout is 0, wait indefinitely.
76+
Returns the port name, or None if timeout expired.
77+
"""
78+
start = time.time()
79+
first = True
80+
while True:
81+
cdc_port = find_ledger_cdc_port(port)
82+
if cdc_port is not None:
83+
if not first:
84+
sys.stdout.write("\n")
85+
sys.stdout.flush()
86+
return cdc_port
87+
if first:
88+
print("Waiting for Ledger CDC debug port...", end="", flush=True)
89+
first = False
90+
else:
91+
sys.stdout.write(".")
92+
sys.stdout.flush()
93+
if timeout and (time.time() - start) >= timeout:
94+
sys.stdout.write("\n")
95+
sys.stdout.flush()
96+
return None
97+
time.sleep(1)
98+
99+
100+
if __name__ == "__main__":
101+
args = get_argparser().parse_args()
102+
103+
output_file = None
104+
if args.output:
105+
try:
106+
output_file = open(args.output, "a", encoding="utf-8")
107+
except OSError:
108+
print("Unable to open file {} for writing.".format(args.output))
109+
sys.exit(1)
110+
111+
try:
112+
while True:
113+
cdc_port = wait_for_cdc_port(args.port)
114+
if cdc_port is None:
115+
print(
116+
"No Ledger CDC debug port found.\n"
117+
"Make sure your Ledger app was compiled with DEBUG_OVER_USB=1\n"
118+
"and that the device is connected and the app is running.\n"
119+
"You can also specify the port manually with --port."
120+
)
121+
sys.exit(1)
122+
123+
print("Connected on {} (baudrate={}).".format(cdc_port, args.baudrate))
124+
print("Press Ctrl+C to stop.\n")
125+
126+
try:
127+
with serial.Serial(cdc_port, args.baudrate, timeout=1) as ser:
128+
while True:
129+
data = ser.readline()
130+
if data:
131+
text = data.decode("utf-8", errors="replace")
132+
timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3]
133+
for line in text.splitlines(True):
134+
stamped = "[{}] {}".format(timestamp, line)
135+
sys.stdout.write(stamped)
136+
sys.stdout.flush()
137+
if output_file:
138+
output_file.write(stamped)
139+
output_file.flush()
140+
except serial.SerialException:
141+
print("\nSerial connection lost. Waiting for device...")
142+
except KeyboardInterrupt:
143+
print("\nStopped.")
144+
finally:
145+
if output_file:
146+
output_file.close()

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ dependencies = [
4343
"nfcpy>=1.0.4",
4444
"bleak>=0.20.1",
4545
"pycryptodome>=3.18.0",
46-
"python-gnupg>=0.5.0"
46+
"python-gnupg>=0.5.0",
47+
"pyserial>=3.5"
4748
]
4849

4950
[tool.setuptools]

0 commit comments

Comments
 (0)