gdbnotes.html
22.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>BBPlayer Debugger</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
</head>
<body>
<h2>BBPlayer Debugger Notes</h2>
<div align="center">$Revision: 1.3 $<br>$Date: 2004/02/19 00:56:27 $<br></div>
<p>This document describes how to use the BBPlayer debugging environment and then describes the changes made to <b>libultra</b> and other tools for GDB support.
<p>The BBPlayer debugging environment is composed of two parts: the <a href="http://www.gnu.org/software/gdb/gdb.html">GNU Debugger (GDB)</a> running on the host machine and a GDB stub running on the BBPlayer. GDB is a command-line based source level debugger, but there are many graphical user interfaces for it. The GDB stub is part of the BBPlayer debug library, <b>libultra_d</b>, and must be included in the program by the developer. The GDB stub has to be running on the BBPlayer before the program can be debugged.
<p>The user of the debugger interacts with GDB, and GDB will communicate the requests to the GDB stub on the BBPlayer. In order for the two components to communicate, there is a third helper tool, <i>mux</i>, that takes the input from GDB via a TCP socket and sends the information using RDB over USB to the BBPlayer. Note that because GDB and mux communicate via TCP, it is not necessary for GDB and mux to be on the same machine.
<p>The BBPlayer debugger supports debugging the main CPU (i.e. the R4300 processor) of the BBPlayer. It allows the user to set breakpoints, step, display registers, variables, and thread information for the main CPU. The debugger does not support debugging of the RCP (the graphics co-processor).
<h3>Using the BB Player debugger</h3>
<h4>Installing the BB Player debugger</h4>
The steps to compile and use the debugger are:<br>
<ul>
<li>Update your tree at the <i>rf/sw</i> level, since the debugger support requires files from both the <i>devroot</i> subtree and the <i>bbplayer</i>
subtree. Make sure that you use <code>cvs update</code> with the <code>-d</code> option to get the two new directories <i>rf/sw/devroot/linux/tools/gdb</i> and <i>rf/sw/bbplayer/libultra/bb/gdb</i>.
</li>
<li>Rebuild at least <i>bbplayer/libultra</i> and <i>bbplayer/tools</i>.
It would be safer to do the full rebuild:</li>
<ul>
<li>cd bbplayer</li>
<li>make clobber</li>
<li>make headers</li>
<li>make exports</li>
<li>make</li>
</ul>
<li>On the host system on which GDB will be running, install the BBPlayer GDB by following these steps:</li>
<ul>
<li>cd devroot/linux</li>
<li>make install</li>
</ul>
<li>The BB Player GDB program <i>ique_gdb</i> should now be added to <i>$ROOT/usr/sbin</i></li>
<li>The debugger has been tested on RH9.0 and Windows XP with Cygwin 1.5.6-1.<br></li>
</li>
</ul>
<h4>Preparing a program for debugging</h4>
<ul>
<li>Create and start the GDB stub threads.
<p>To include the GDB stub in the program, the programmer should call <code>gdbInit()</code> to initialize and start the GDB stub threads.
<br><br>
<b>GDB example</b>:
<code><pre>
gdbInit();</pre></code>
The call to <code>gdbInit</code> effectively starts a thread with <code>gdbMain</code> as its entry point, a priority of <code>OS_PRIORITY_GDB</code> and a thread ID of <code>OS_TID_GDBMAIN</code>. The following code fragment shows what <code>gdbInit()</code> does:
<code><pre>
static OSThread gdbThread;
static u64 gdbStack[GDB_STACKSIZE/sizeof(u64)] __attribute__ ((aligned (8)));
osCreateThread(&gdbThread, OS_TID_GDBMAIN, gdbMain, (void *)0,
(void *)(gdbStack + GDB_STACKSIZE/sizeof(u64)),(OSPri) OS_PRIORITY_GDB );
osStartThread(&gdbThread);</pre></code>
For backward compatibility with a program written to be debugged using RMON, creating a thread that calls <code>rmonMain</code> will create threads for the GDB stub instead.
<br><br>
<b>RMON example</b> (deprecated):
<code><pre>
static OSThread rmonThread;
static u64 rmonStack[RMON_STACKSIZE/sizeof(u64)] __attribute__ ((aligned (8)));
osCreateThread(&rmonThread, OS_TID_RMONMAIN, rmonMain, (void *)0,
(void *)(rmonStack + RMON_STACKSIZE/8), (OSPri) OS_PRIORITY_RMON );
osStartThread(&rmonThread);</pre></code>
</li>
<li>To distinguish between threads, give each thread an unique ID. This is not enforced by the OS, but unless every thread has an unique ID, it will be difficult to determine which thread is which.</li><br>
<li>Restrict user thread priorities to the range 1 to 127 (OS_PRIORITY_APPMAX), with 127 being the highest priority. The idle thread must has a priority of 0 (OS_PRIORITY_IDLE). This is not enforced by the OS, but is necessary for the debugger to function properly. The debugger determines which threads are user threads based on the priority, and if the thread priorities are not set appropriately, the debugger may not function and may cause the system to hang.
<p>Some possible scenarios are:
<ul>
<li>If the idle thread priority is not set to 0, the debugger will not be able to identify which thread is the idle thread. If the debugger suspends the idle thread, the system will lock up. To prevent this from happening, the debugger will not suspend any user thread, but may not function correctly.</li>
<li>If the user thread priority is too high, the debugger and USB threads may never run. It will be impossible to debug the program then.</li>
</ul>
</li><br>
<li>Compile a debug version and link with the libultra debugging library</li>
<ul>
<li>Use the <b>-g</b> option with gcc to compile a debug version of the program</li>
<li>Link with <b>libultra_d</b></li>
</ul>
</ul>
<h4>Debugging your program</h4>
Connect the cable between your linux host and the BB player and follow the steps below to start a debugging session with GDB:
<ul>
<li>Start mux on the host machine so that it is ready for GDB to connect to it via TCP. By default, mux will listen on TCP port 8088 for GDB debugger messages. You can select a different port by using the <code>--debugport</code> option</li>
<li>Start the program to be debugged on the BBPlayer</li>
<li>Start the GDB debugger on the host machine (or another machine that has network connectivity to the host machine)
<ul>
<li><code>
ique_gdb <elf file> <br>
</code></li>
</ul>
See <a href="#Usage GUI">Graphical Front Ends to GDB</a> for how to run <em>ique_gdb</em> with
a GUI.
</li>
<li>Connect to the BBPlayer from GDB via <i>mux</i>.
<br>
At the GDB prompt type <code>target remote <machine name>:<port></code>.
For instance, if <i>mux</i> and GDB are running on the same machine with the default port:
<ul>
<li>target remote localhost:8088
</ul>
</li>
</ul>
Once GDB has attached to the target, all user threads will be stopped and the program can now be debugged. See the <a href="http://sources.redhat.com/gdb/current/onlinedocs/gdb_toc.html"> GDB User Guide</a> for more information on how to use GDB. When you are done debugging, use the "<code>detach</code>" command to stop the GDB debugging session and resume execution of the program. If you want to leave the application stopped so another debugger can connect to it, use the "<code>disconnect</code>" command to disconnect from the BB player. Only one debugger can connect to the BB Player at a time, so make sure that you've disconnected or detach from the BB Player when you are finished debugging.
<a name="Usage GUI"></a>
<h4>Graphical Front Ends to GDB</h4>
For a visual debugging environment, there are several applications that can supplies a graphical front-end to GDB:
<ul>
<li><a href=http://www.gnu.org/software/ddd><b>DDD</b></a> (Data Display Debugger)<br>
<b>Note:</b> DDD does not display all the thread information that the BB Player provides.
Use the command <code>"info threads"</code> in the gdb console window to get the full information.
<pre>Usage: <code>ddd --debugger <gdb> <elf file></code></pre>
<pre>Example: <code>ddd --debugger "$ROOT/usr/sbin/ique_gdb" <elf file></code></pre>
</li>
<li><a href=http://libre.act-europe.fr/gvd><b>GVD</b></a> (GNU Visual Debugger)</a><br>
GVD is integrated into <a href=http://libre.act-europe.fr/gps/>GPS</a>
(GNAT Programming System).<br>
<b>Note:</b> Do not use the code disassembly feature in GVD. That does not work well with
the BB Player.
<pre>Usage: <code>gps --debugger=<gdb> --debug=<elf file></code></pre>
<pre>Example: <code>gps --debugger="$ROOT/usr/sbin/ique_gdb" --debug=<elf file></code></pre>
</li>
</ul>
<h3>Limitations and Guidelines</h3>
<p>Because the BB Player debugger runs in the same memory space as the user program and its reliance on RDB/USB for communication with GDB, the BB Player debugger has several limitations in how it can be used. These limitations imply that the user must be very careful in how the debugger is used.
<p><b>When using the debugger, the following should be avoided:</b>
<ul>
<li>Never try to stop or step into the GDB stub threads or the USB threads. If you attempt to do this, and one of the GDB stub thread or USB thread hits a break, then the debugger will cease to function.</li>
<br>
<li>Never try to stop, step into, or set breakpoints in the idle thread. If the idle thread stops, it will cause the entire system to lock up.</li>
<br>
<li>Never try to put a breakpoint in or step into an operating system function. Because the system functions are shared by GDB stub, the USB thread, placing a breakpoint or stepping into these functions may also cause the GDB stub thread or USB thread to hit a break.</li>
<br>
<li>Do not use watchpoints in GDB. GDB uses software watchpoints that will automatically set breakpoints in the running program and will very likely set breakpoints in the GDB stub and USB threads, causing the debugger to stop working.</li>
<br>
<li>Do not use <code>osSetEventMesg</code> to register message queues for <code>OS_EVENT_FAULT</code> or <code>OS_EVENT_CPU_BREAK</code>
events if you want to use the GDB debugger. Since only one message queue may be associated with one event, the GDB stub will defer
to the user program if the user program attempts to register messages queues for these events. This means that the GDB stub will not
get notified if there is a CPU fault or a breakpoint. If the GDB stub does not get notified of breakpoints, you cannot use GDB
to set breakpoints and to step through your problem. If the GDB stub does not get notified of CPU faults, then GDB cannot inform
you when an CPU fault occurs.</li>
</ul>
<p><b>The debugger will not be able to debug all programs or problems.</b>
<ul>
<li>It cannot be used to debug problems with the RDB or USB since it relies on these two components to function</li>
<br>
<li>It is of limited use when debugging application startup. Since the GDB stub must be running before the program can be debugged, it is impossible to debug anything that happens before that.
<p>
To enable as much debugging of the start-up code as possible, the developer should minimize the boot procedure and have the thread that is started by the boot function do the following (in order):
<ul>
<li>Call <code>gdbInit()</code>. This will create and start the GDB stub threads and allow the program to be debugged.</li>
<li>Create but do not start the rest of the threads in the program. This will allow the program to start up in a stopped state. The developer can then attach to the program using GDB and start up the threads within the debugger (by using the continue command). This gives the developer a chance to set breakpoints at the begining of the thread and to watch the startup of the thread.</li>
<li>Lower the priority of the thread to 0 and have it become the idle thread.</li>
</ul>
</li>
<br>
<li>Because the debugger changes the timing of the application and the scheduling of the threads, the debugger is also of limited use when debugging multithreaded problems (i.e. deadlock, race conditions, etc).</li>
<br>
<li>Since the software program and the debug support are all in the same memory space, it is possible for a bug (i.e. buffer overflow) in the software program to stomp over the GDB stub. At which point, the debugger will not be of use any longer.</li>
</ul>
<p><b>The following are not supported by the BB Player debugger:</b>
<ul>
<li>Debugging the Reality Coprocessor.</li>
<li>Watchpoints.</li>
<li>Sending signals to the BB Player.</li>
</ul>
<a name="Issues"></a>
<h3>Known Issues</h3>
<ol>
<li>If the PC is invalid (e.g. by trying to jump to an invalid function pointer), then
the stack backtrack is unavailable.
<em>ique_gdb</em> is based on GDB 6.0 and there appears to be a problem with GDB 6.0 frames support for the MIPS processor. Once GDB 6.1 becomes available, we will check whether the new version fixes the problem.</li>
<br>
<li>The BB Player can get into a state where it stops communicating with the <em>ique_gdb</em> program. If you encounter this, restart the application, <em>ique_gdb</em>, <em>mux</em> and try again.</li>
<br>
<li>If more than one GDB tries to connect to the BB Player, the second GDB will just hang. Only one GDB can connect to the BB Player at a time. Do not try to use multiple GDBs at the same time.</li>
<br>
<li>If multiple threads are faulted or stopped due to different causes, only one cause can be reported back to GDB. The GDB Remote Serial Protocol only allows for one signal to be sent to GDB when the program stops.</li>
</ol>
<h3>Implementation</h3>
<h4>GDB Files Changed</h4>
The GDB we are using is based on GDB 6.0 and compiled with mips-linux as the target. Although we wanted to avoid any changes to GDB itself, the following changes were made to GDB:
<ul>
<li>
<b>gdb/mdebugread.c</b> - Modified to set the high pc of the boot/entry section of the code to be the same as the low pc. The high pc didn't seem to be correct and was causing the stack backtrace to stop too early.
</li>
</ul>
The GDB code resides in <i>rf/sw/devroot/linux/tools/gdb</i>.
<h4>Libultra Files Changed</h4>
<p>The GDB stub was added as a new component of libultra and lives in <i>rf/sw/bbplayer/libultra/bb/gdb</i>. The GDB stub is modeled after the RMON debug utility from Nintendo. The GDB stub is composed of two threads: the gdbMain thread that is responsible for monitoring breaks/faults and the gdbIO thread that is responsible for communicating with GDB. The GDB stub communicates with GDB using the <a href="http://sources.redhat.com/gdb/current/onlinedocs/gdb_33.html">GDB Remote Serial Protocol</a>. See section on <a href=#BB_GDB_RSP>"BBPlayer support for the GDB Remote Serial Protocol"</a> for a summary of the packet types supported by the GDB stub.
<p>The RMON functions are deprecated and is no longer linked into libultra. The following RMON symbols are aliased to their GDB counterparts for backward compatibility:
<table align=center cellspacing=3 cellpadding=3 border=1>
<tr><th>RMON</th><th>GDB</th></tr>
<tr><td><code>rmonMain</code></td>
<td><code>gdbMain</code></td></tr>
<tr><td><code>rmonPrintf</code></td>
<td><code>osSyncPrintf</code></td></tr>
<tr><td><code>RMON_DBG_BUF_SIZE</code></td>
<td><code>GDB_DBG_BUF_SIZE</code></td></tr>
<tr><td><code>RMON_DBG_STACK_SIZE</code></td>
<td><code>GDB_STACKSIZE</code></td></tr>
<tr><td><code>OS_PRIORITY_RMON</code></td>
<td><code>OS_PRIORITY_GDB</code></td></tr>
<tr><td><code>OS_TID_RMONMAIN</code></td>
<td><code>OS_TID_GDBMAIN</code></td></tr>
<tr><td><code>OS_TID_RMONIO</code></td>
<td><code>OS_TID_GDBIO</code></td></tr>
</table>
<p>The USB driver code lives in <i>libultra/bb/usb</i>. The main routine to look at there is in:
<ul>
<li><b>rdb_slave.c</b> - the routine<i> rdb_ip6_input</i> contains the code for USB that is analogous to the interrupt code in <i>nintendo/exception/exceptasm.s</i> for processing incoming RDB packets. It has been updated to understand RDB messages for debugging (i.e. RDB_TYPE_HtoG_DEBUG and RDB_TYPE_HtoG_DEBUG_DONE). Data from the GDB to the GDB stub are identified as RDB_TYPE_HtoG_DEBUG with a RDB_TYPE_HtoG_DEBUG_DONE packet at the end to indicate that the gdbIO thread can now be signaled to process the new GDB data.
</li>
</ul>
<p>Various other files were also changed in libultra to give each system thread a unique thread ID. A summary of the system threads changed is given below:
<table align=center cellspacing=3 cellpadding=3 border=1>
<tr><th>File (where thread created)</th>
<th>Thread</th>
<th>Thread ID</th>
<th>Thread Priority</th>
</tr>
<tr><td rowspan=2><code>libultra/bb/usb/usbinit.c</code></td>
<td><code>__osBbUsbMgrProc</code></td>
<td><code>OS_USB_TID_BASE (3141)</code></td>
<td><code>OS_USB_THREAD_PRI_HOST (230)</code></td>
</tr>
<tr>
<td><code>__osBbUsbMgrProc</code></td>
<td><code>OS_USB_TID_BASE+1 (3142)</code></td>
<td><code>OS_USB_THREAD_PRI_DEV (232)</code></td>
</tr>
<tr><td rowspan=2><code>libultra/bb/gdb/gdbmain.c</code></td>
<td><code>gdbMain</code></td>
<td><code>OS_TID_GDBMAIN (3201)</code></td>
<td><code>OS_PRIORITY_GDB (250)</code></td>
</tr>
<tr>
<td><code>__gdbIOHandler</code></td>
<td><code>OS_TID_GDBIO (3202)</code></td>
<td><code>255</code></td>
</tr>
<tr><td rowspan=1><code>libultra/monegi/debug/profile.c
</code></td>
<td><code>__osProfileIO</code></td>
<td><code>OS_TID_PROFILEIO (3251)</code></td>
<td><code>129</code></td>
</tr>
<tr><td rowspan=2><code>libultra/nintendo/pi/pimgr.c</code></td>
<td><code>__osDevMgrMain</code></td>
<td><code>OS_TID_PIMGR (3301)</code></td>
<td><code>OS_PRIORITY_PIMGR (150)</code></td>
</tr>
<tr>
<td><code>ramromMain</code></td>
<td><code>OS_TID_RAMROM (3302)</code></td>
<td><code>OS_PRIORITY_PIMGR-1 (149)</code></td>
</tr>
<tr><td rowspan=1><code>libultra/monegi/vi/vimgr.c</code></td>
<td><code>viMgrMain</code></td>
<td><code>OS_TID_VIMGR (3401)</code></td>
<td><code>OS_PRIORITY_VIMGR (254)</code></td>
</tr>
</table>
<h4>Utilities Changed</h4>
<ul>
<li>
<b>tools/mux/mux.c</b> - Added support for the debug stream to communicate with the BBPlayer and with GDB via a TCP socket. Added command line option <code>--debugport <port></code> to select which TCP port to use as the debug port.
</li>
</ul>
<br>
<a name="BB_GDB_RSP"></a>
<h3>BBPlayer support for the GDB Remote Serial Protocol</h3>
<p>The following packet types are support by the BBPlayer's GDB stub:
<p>
<table align=center cellspacing=3 cellpadding=3 border=1>
<tr><th>GDB Request</th>
<th>Reply</th>
<th>Action</th>
</tr>
<tr><td><code>? -- last signal</code></td>
<td><code>T<i>signal</i>thread:<i>tid</i>;</code></td>
<td></td>
<tr><td><code>c<i>addr</i> -- continue at address</code>
<td><code>T<i>signal</i>thread:<i>tid</i>;</code>
<br><code>E03</code> - Invalid Address</td>
<td>The GDB stub will restart the selected thread (usually all user threads),
and wait until a breakpoint or it gets a Ctrl-C from GDB to respond.</td>
</tr>
<tr><td><code>s<i>addr</i> -- step at address</code>
<td><code>T<i>signal</i>thread:<i>tid</i>;</code>
<br><code>E03</code> - Invalid Address</td>
<td>The GDB stub will emulate a single step by placing temporary points in the code.
The selected thread (usually all user threads) will be restarted
and wait until a breakpoint or it gets a Ctrl-C from GDB to respond.</td>
</tr>
<tr><td><code>g -- read general registers</code></td>
<td><code><i>registers</i></code>
<br><code>E02</code> - if no thread</td>
<td>The GDB stub will assemble the registers from the selected thread.</td>
</tr>
<tr><td><code>Hc<i>tid</i>, Hg<i>tid</i> -- Set thread</code></td>
<td><code>OK - Success</code>
<br><code>E02</code> - if no thread with given <i>tid</i></td>
<td>The GDB stub will set the selected thread for subsequent operations.</td>
</tr>
<tr><td><code>k -- Kill thread</code></td>
<td></td>
<td>The GDB stub will stop all user threads.</td>
</tr>
<tr><td><code>D -- Detach</code></td>
<td></td>
<td>GDB is detaching from the BB Player. The GDB stub will clear all breakpoints and resume execution of all threads.</td>
</tr>
<tr><td><code>m<i>addr</i>,<i>length</i> -- Read memory</code></td>
<td><code><i>memory contents</i></code>
<br><code>E01</code> - Bad packet format
<br><code>E03</code> - Invalid address</td>
<td>The GDB stub will verify the memory address is valid and return the memory contents.</td>
</tr>
<tr><td><code>M<i>addr</i>,<i>length</i>:<i>data</i> -- Write into memory</code></td>
<td><code>OK - Success</code>
<br><code>E01</code> - Bad packet format
<br><code>E03</code> - Invalid Address</td>
<td>The GDB stub will verify the memory address is valid and set the memory contents.</td>
</tr>
<tr><td><code>P<i>reg</i>=<i>value</i> -- Write register</code></td>
<td><code>OK - Success</code>
<br><code>E01</code> - Bad packet format
<br><code>E02</code> - No thread
<br><code>E03</code> - Invalid register number</td>
<td>The GDB stub will verify the register number is valid and set the register value.</td>
</tr>
<tr><td><code>z0<i>addr</i>,<i>length</i> -- remove memory breakpoint at address</code>
<td rowspan=2><code>OK - Success</code>
<br><code>E01</code> - Bad packet format
<br><code>E03</code> - Invalid Address
<br><code>E05</code> - No more slots
<br><code>E06</code> - Operation failed
</td>
<td rowspan=2>The GDB stub will insert/remove the breakpoint at the specified address.</td>
</tr>
<tr>
<td><code>Z0<i>addr</i>,<i>length</i> -- insert memory breakpoint at address</code>
</tr>
<tr><td><code>qC -- current thread</code></td>
<td><code>QC<i>tid</i></code></td>
<td>The GDB stub will return the current thread ID.</td>
</tr>
<tr><td><code>qfThreadInfo -- all thread ids</code></td>
<td rowspan=2><code>m<i>tid</i>,<i>tid</i>,...l</code></td>
<td rowspan=2>The GDB stub will return the list of thread ids (terminated by an lower case el). The list may be broken down into several reply packets.</td>
</tr>
<tr><td><code>qsThreadInfo -- subsequent thread ids</code></td>
</tr>
<tr><td><code>qThreadExtraInfo,<i>tid</i></code></td>
<td><code><i>hex encoded ASCII string</i></code><br>
<br><code>E03</code> - Invalid Address</td>
<td>The GDB stub will return a hex encoded string with information about the thread ID, priority, and state.</td>
</tr>
</table>
</body>
</html>