| LLDB has added new GDB server packets to better support multi-threaded and |
| remote debugging. Why? Normally you need to start the correct GDB and the |
| correct GDB server when debugging. If you have mismatch, then things go wrong |
| very quickly. LLDB makes extensive use of the GDB remote protocol and we |
| wanted to make sure that the experience was a bit more dynamic where we can |
| discover information about a remote target with having to know anything up |
| front. We also ran into performance issues with the existing GDB remote |
| protocol that can be overcome when using a reliable communications layer. |
| Some packets improve performance, others allow for remote process launching |
| (if you have an OS), and others allow us to dynamically figure out what |
| registers a thread might have. Again with GDB, both sides pre-agree on how the |
| registers will look (how many, their register number,name and offsets). We |
| prefer to be able to dynamically determine what kind of architecture, os and |
| vendor we are debugging, as well as how things are laid out when it comes to |
| the thread register contexts. Below are the details on the new packets we have |
| added above and beyond the standard GDB remote protocol packets. |
| |
| //---------------------------------------------------------------------- |
| // "QStartNoAckMode" |
| // |
| // BRIEF |
| // Try to enable no ACK mode to skip sending ACKs and NACKs. |
| // |
| // PRIORITY TO IMPLEMENT |
| // High. Any GDB remote server that can implement this should if the |
| // connection is reliable. This improves packet throughput and increases |
| // the performance of the connection. |
| //---------------------------------------------------------------------- |
| Having to send an ACK/NACK after every packet slows things down a bit, so we |
| have a way to disable ACK packets to mimize the traffic for reliable |
| communication interfaces (like sockets). Below GDB or LLDB will send this |
| packet to try and disable ACKs. All lines that start with "send packet: " are |
| from GDB/LLDB, and all lines that start with "read packet: " are from the GDB |
| remote server: |
| |
| send packet: $QStartNoAckMode#b0 |
| read packet: + |
| read packet: $OK#9a |
| send packet: + |
| |
| |
| |
| //---------------------------------------------------------------------- |
| // "A" - launch args packet |
| // |
| // BRIEF |
| // Launch a program using the supplied arguments |
| // |
| // PRIORITY TO IMPLEMENT |
| // Low. Only needed if the remote target wants to launch a target after |
| // making a connection to a GDB server that isn't already connected to |
| // an inferior process. |
| //---------------------------------------------------------------------- |
| |
| We have added support for the "set program arguments" packet where we can |
| startup a connection to a remote server and then later supply the path to the |
| executable and the arguments to use when executing: |
| |
| GDB remote docs for this: |
| |
| set program arguments(reserved) Aarglen,argnum,arg,... |
| |
| Where A is followed by the length in bytes of the hex encoded argument, |
| followed by an argument integer, and followed by the ASCII characters |
| converted into hex bytes foreach arg |
| |
| send packet: $A98,0,2f566f6c756d65732f776f726b2f67636c6179746f6e2f446f63756d656e74732f7372632f6174746163682f612e6f7574#00 |
| read packet: $OK#00 |
| |
| The above packet helps when you have remote debugging abilities where you |
| could launch a process on a remote host, this isn't needed for bare board |
| debugging. |
| |
| //---------------------------------------------------------------------- |
| // "QEnvironment:NAME=VALUE" |
| // |
| // BRIEF |
| // Setup the environment up for a new child process that will soon be |
| // launched using the "A" packet. |
| // |
| // PRIORITY TO IMPLEMENT |
| // Low. Only needed if the remote target wants to launch a target after |
| // making a connection to a GDB server that isn't already connected to |
| // an inferior process. |
| //---------------------------------------------------------------------- |
| |
| Both GDB and LLDB support passing down environment variables. Is it ok to |
| respond with a "$#00" (unimplemented): |
| |
| send packet: $QEnvironment:ACK_COLOR_FILENAME=bold yellow#00 |
| read packet: $OK#00 |
| |
| This packet can be sent one or more times _prior_ to sending a "A" packet. |
| |
| //---------------------------------------------------------------------- |
| // "QSetSTDIN:<ascii-hex-path>" |
| // "QSetSTDOUT:<ascii-hex-path>" |
| // "QSetSTDERR:<ascii-hex-path>" |
| // |
| // BRIEF |
| // Setup where STDIN, STDOUT, and STDERR go prior to sending an "A" |
| // packet. |
| // |
| // PRIORITY TO IMPLEMENT |
| // Low. Only needed if the remote target wants to launch a target after |
| // making a connection to a GDB server that isn't already connected to |
| // an inferior process. |
| //---------------------------------------------------------------------- |
| |
| When launching a program through the GDB remote protocol with the "A" packet, |
| you might also want to specify where stdin/out/err go: |
| |
| QSetSTDIN:<ascii-hex-path> |
| QSetSTDOUT:<ascii-hex-path> |
| QSetSTDERR:<ascii-hex-path> |
| |
| These packets must be sent _prior_ to sending a "A" packet. |
| |
| //---------------------------------------------------------------------- |
| // "QSetWorkingDir:<ascii-hex-path>" |
| // |
| // BRIEF |
| // Set the working directory prior to sending an "A" packet. |
| // |
| // PRIORITY TO IMPLEMENT |
| // Low. Only needed if the remote target wants to launch a target after |
| // making a connection to a GDB server that isn't already connected to |
| // an inferior process. |
| //---------------------------------------------------------------------- |
| |
| Or specify the working directory: |
| |
| QSetWorkingDir:<ascii-hex-path> |
| |
| This packet must be sent _prior_ to sending a "A" packet. |
| |
| //---------------------------------------------------------------------- |
| // "QSetDisableASLR:<bool>" |
| // |
| // BRIEF |
| // Enable or disable ASLR on the next "A" packet. |
| // |
| // PRIORITY TO IMPLEMENT |
| // Low. Only needed if the remote target wants to launch a target after |
| // making a connection to a GDB server that isn't already connected to |
| // an inferior process and if the target supports disabling ASLR |
| // (Address space layout randomization). |
| //---------------------------------------------------------------------- |
| |
| Or control if ASLR is enabled/disabled: |
| |
| send packet: QSetDisableASLR:1 |
| read packet: OK |
| |
| send packet: QSetDisableASLR:0 |
| read packet: OK |
| |
| This packet must be sent _prior_ to sending a "A" packet. |
| |
| //---------------------------------------------------------------------- |
| // "qRegisterInfo<hex-reg-id>" |
| // |
| // BRIEF |
| // Discover register information from the remote GDB server. |
| // |
| // PRIORITY TO IMPLEMENT |
| // High. Any target that can self describe its registers, should do so. |
| // This means if new registers are ever added to a remote target, they |
| // will get picked up automatically, and allows registers to change |
| // depending on the actual CPU type that is used. |
| //---------------------------------------------------------------------- |
| |
| With LLDB, for register information, remote GDB servers can add support for |
| the "qRegisterInfoN" packet where "N" is a zero based register number that |
| must start at zero and increase by one for each register that is supported. |
| The response is done in typical GDB remote fashion where a serious of |
| "KEY:VALUE;" pairs are returned. An example for the x86_64 registers is |
| included below: |
| |
| send packet: $qRegisterInfo0#00 |
| read packet: $name:rax;bitsize:64;offset:0;encoding:uint;format:hex;set:General Purpose Registers;gcc:0;dwarf:0;#00 |
| send packet: $qRegisterInfo1#00 |
| read packet: $name:rbx;bitsize:64;offset:8;encoding:uint;format:hex;set:General Purpose Registers;gcc:3;dwarf:3;#00 |
| send packet: $qRegisterInfo2#00 |
| read packet: $name:rcx;bitsize:64;offset:16;encoding:uint;format:hex;set:General Purpose Registers;gcc:2;dwarf:2;#00 |
| send packet: $qRegisterInfo3#00 |
| read packet: $name:rdx;bitsize:64;offset:24;encoding:uint;format:hex;set:General Purpose Registers;gcc:1;dwarf:1;#00 |
| send packet: $qRegisterInfo4#00 |
| read packet: $name:rdi;bitsize:64;offset:32;encoding:uint;format:hex;set:General Purpose Registers;gcc:5;dwarf:5;#00 |
| send packet: $qRegisterInfo5#00 |
| read packet: $name:rsi;bitsize:64;offset:40;encoding:uint;format:hex;set:General Purpose Registers;gcc:4;dwarf:4;#00 |
| send packet: $qRegisterInfo6#00 |
| read packet: $name:rbp;alt-name:fp;bitsize:64;offset:48;encoding:uint;format:hex;set:General Purpose Registers;gcc:6;dwarf:6;generic:fp;#00 |
| send packet: $qRegisterInfo7#00 |
| read packet: $name:rsp;alt-name:sp;bitsize:64;offset:56;encoding:uint;format:hex;set:General Purpose Registers;gcc:7;dwarf:7;generic:sp;#00 |
| send packet: $qRegisterInfo8#00 |
| read packet: $name:r8;bitsize:64;offset:64;encoding:uint;format:hex;set:General Purpose Registers;gcc:8;dwarf:8;#00 |
| send packet: $qRegisterInfo9#00 |
| read packet: $name:r9;bitsize:64;offset:72;encoding:uint;format:hex;set:General Purpose Registers;gcc:9;dwarf:9;#00 |
| send packet: $qRegisterInfoa#00 |
| read packet: $name:r10;bitsize:64;offset:80;encoding:uint;format:hex;set:General Purpose Registers;gcc:10;dwarf:10;#00 |
| send packet: $qRegisterInfob#00 |
| read packet: $name:r11;bitsize:64;offset:88;encoding:uint;format:hex;set:General Purpose Registers;gcc:11;dwarf:11;#00 |
| send packet: $qRegisterInfoc#00 |
| read packet: $name:r12;bitsize:64;offset:96;encoding:uint;format:hex;set:General Purpose Registers;gcc:12;dwarf:12;#00 |
| send packet: $qRegisterInfod#00 |
| read packet: $name:r13;bitsize:64;offset:104;encoding:uint;format:hex;set:General Purpose Registers;gcc:13;dwarf:13;#00 |
| send packet: $qRegisterInfoe#00 |
| read packet: $name:r14;bitsize:64;offset:112;encoding:uint;format:hex;set:General Purpose Registers;gcc:14;dwarf:14;#00 |
| send packet: $qRegisterInfof#00 |
| read packet: $name:r15;bitsize:64;offset:120;encoding:uint;format:hex;set:General Purpose Registers;gcc:15;dwarf:15;#00 |
| send packet: $qRegisterInfo10#00 |
| read packet: $name:rip;alt-name:pc;bitsize:64;offset:128;encoding:uint;format:hex;set:General Purpose Registers;gcc:16;dwarf:16;generic:pc;#00 |
| send packet: $qRegisterInfo11#00 |
| read packet: $name:rflags;alt-name:flags;bitsize:64;offset:136;encoding:uint;format:hex;set:General Purpose Registers;#00 |
| send packet: $qRegisterInfo12#00 |
| read packet: $name:cs;bitsize:64;offset:144;encoding:uint;format:hex;set:General Purpose Registers;#00 |
| send packet: $qRegisterInfo13#00 |
| read packet: $name:fs;bitsize:64;offset:152;encoding:uint;format:hex;set:General Purpose Registers;#00 |
| send packet: $qRegisterInfo14#00 |
| read packet: $name:gs;bitsize:64;offset:160;encoding:uint;format:hex;set:General Purpose Registers;#00 |
| send packet: $qRegisterInfo15#00 |
| read packet: $name:fctrl;bitsize:16;offset:176;encoding:uint;format:hex;set:Floating Point Registers;#00 |
| send packet: $qRegisterInfo16#00 |
| read packet: $name:fstat;bitsize:16;offset:178;encoding:uint;format:hex;set:Floating Point Registers;#00 |
| send packet: $qRegisterInfo17#00 |
| read packet: $name:ftag;bitsize:8;offset:180;encoding:uint;format:hex;set:Floating Point Registers;#00 |
| send packet: $qRegisterInfo18#00 |
| read packet: $name:fop;bitsize:16;offset:182;encoding:uint;format:hex;set:Floating Point Registers;#00 |
| send packet: $qRegisterInfo19#00 |
| read packet: $name:fioff;bitsize:32;offset:184;encoding:uint;format:hex;set:Floating Point Registers;#00 |
| send packet: $qRegisterInfo1a#00 |
| read packet: $name:fiseg;bitsize:16;offset:188;encoding:uint;format:hex;set:Floating Point Registers;#00 |
| send packet: $qRegisterInfo1b#00 |
| read packet: $name:fooff;bitsize:32;offset:192;encoding:uint;format:hex;set:Floating Point Registers;#00 |
| send packet: $qRegisterInfo1c#00 |
| read packet: $name:foseg;bitsize:16;offset:196;encoding:uint;format:hex;set:Floating Point Registers;#00 |
| send packet: $qRegisterInfo1d#00 |
| read packet: $name:mxcsr;bitsize:32;offset:200;encoding:uint;format:hex;set:Floating Point Registers;#00 |
| send packet: $qRegisterInfo1e#00 |
| read packet: $name:mxcsrmask;bitsize:32;offset:204;encoding:uint;format:hex;set:Floating Point Registers;#00 |
| send packet: $qRegisterInfo1f#00 |
| read packet: $name:stmm0;bitsize:80;offset:208;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:33;dwarf:33;#00 |
| send packet: $qRegisterInfo20#00 |
| read packet: $name:stmm1;bitsize:80;offset:224;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:34;dwarf:34;#00 |
| send packet: $qRegisterInfo21#00 |
| read packet: $name:stmm2;bitsize:80;offset:240;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:35;dwarf:35;#00 |
| send packet: $qRegisterInfo22#00 |
| read packet: $name:stmm3;bitsize:80;offset:256;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:36;dwarf:36;#00 |
| send packet: $qRegisterInfo23#00 |
| read packet: $name:stmm4;bitsize:80;offset:272;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:37;dwarf:37;#00 |
| send packet: $qRegisterInfo24#00 |
| read packet: $name:stmm5;bitsize:80;offset:288;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:38;dwarf:38;#00 |
| send packet: $qRegisterInfo25#00 |
| read packet: $name:stmm6;bitsize:80;offset:304;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:39;dwarf:39;#00 |
| send packet: $qRegisterInfo26#00 |
| read packet: $name:stmm7;bitsize:80;offset:320;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:40;dwarf:40;#00 |
| send packet: $qRegisterInfo27#00 |
| read packet: $name:xmm0;bitsize:128;offset:336;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:17;dwarf:17;#00 |
| send packet: $qRegisterInfo28#00 |
| read packet: $name:xmm1;bitsize:128;offset:352;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:18;dwarf:18;#00 |
| send packet: $qRegisterInfo29#00 |
| read packet: $name:xmm2;bitsize:128;offset:368;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:19;dwarf:19;#00 |
| send packet: $qRegisterInfo2a#00 |
| read packet: $name:xmm3;bitsize:128;offset:384;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:20;dwarf:20;#00 |
| send packet: $qRegisterInfo2b#00 |
| read packet: $name:xmm4;bitsize:128;offset:400;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:21;dwarf:21;#00 |
| send packet: $qRegisterInfo2c#00 |
| read packet: $name:xmm5;bitsize:128;offset:416;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:22;dwarf:22;#00 |
| send packet: $qRegisterInfo2d#00 |
| read packet: $name:xmm6;bitsize:128;offset:432;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:23;dwarf:23;#00 |
| send packet: $qRegisterInfo2e#00 |
| read packet: $name:xmm7;bitsize:128;offset:448;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:24;dwarf:24;#00 |
| send packet: $qRegisterInfo2f#00 |
| read packet: $name:xmm8;bitsize:128;offset:464;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:25;dwarf:25;#00 |
| send packet: $qRegisterInfo30#00 |
| read packet: $name:xmm9;bitsize:128;offset:480;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:26;dwarf:26;#00 |
| send packet: $qRegisterInfo31#00 |
| read packet: $name:xmm10;bitsize:128;offset:496;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:27;dwarf:27;#00 |
| send packet: $qRegisterInfo32#00 |
| read packet: $name:xmm11;bitsize:128;offset:512;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:28;dwarf:28;#00 |
| send packet: $qRegisterInfo33#00 |
| read packet: $name:xmm12;bitsize:128;offset:528;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:29;dwarf:29;#00 |
| send packet: $qRegisterInfo34#00 |
| read packet: $name:xmm13;bitsize:128;offset:544;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:30;dwarf:30;#00 |
| send packet: $qRegisterInfo35#00 |
| read packet: $name:xmm14;bitsize:128;offset:560;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:31;dwarf:31;#00 |
| send packet: $qRegisterInfo36#00 |
| read packet: $name:xmm15;bitsize:128;offset:576;encoding:vector;format:vector-uint8;set:Floating Point Registers;gcc:32;dwarf:32;#00 |
| send packet: $qRegisterInfo37#00 |
| read packet: $name:trapno;bitsize:32;offset:696;encoding:uint;format:hex;set:Exception State Registers;#00 |
| send packet: $qRegisterInfo38#00 |
| read packet: $name:err;bitsize:32;offset:700;encoding:uint;format:hex;set:Exception State Registers;#00 |
| send packet: $qRegisterInfo39#00 |
| read packet: $name:faultvaddr;bitsize:64;offset:704;encoding:uint;format:hex;set:Exception State Registers;#00 |
| send packet: $qRegisterInfo3a#00 |
| read packet: $E45#00 |
| |
| As we see above we keep making subsequent calls to the remote server to |
| discover all registers by increasing the number appended to qRegisterInfo and |
| we get a response back that is a series of "key=value;" strings. The keys and |
| values are detailed below: |
| |
| Key Value |
| ========== ================================================================ |
| name The primary register name as a string ("rbp" for example) |
| |
| alt-name An alternate name for a register as a string ("fp" for example for |
| the above "rbp") |
| |
| bitsize Size in bits of a register (32, 64, etc) |
| |
| offset The offset within the "g" and "G" packet of the register data for |
| this register |
| |
| encoding The encoding type of the register which must be one of: |
| |
| uint (unsigned integer) |
| sint (signed integer) |
| ieee754 (IEEE 754 float) |
| vector (vector regsiter) |
| |
| format The preferred format for display of this register. The value must |
| be one of: |
| |
| binary |
| decimal |
| hex |
| float |
| vector-sint8 |
| vector-uint8 |
| vector-sint16 |
| vector-uint16 |
| vector-sint32 |
| vector-uint32 |
| vector-float32 |
| vector-uint128 |
| |
| set The regiter set name as a string that this register belongs to. |
| |
| gcc The GCC compiler registers number for this register (used for |
| EH frame and other compiler information that is encoded in the |
| executable files). |
| |
| NOTE: If the compiler doesn't have a register number for this |
| register, this key/value pair should be omitted. |
| |
| dwarf The DWARF register number for this register that is used for this |
| register in the debug information. |
| |
| NOTE: If the compiler doesn't have a register number for this |
| register, this key/value pair should be omitted. |
| |
| generic If the register is a generic register that most CPUs have, classify |
| it correctly so the debugger knows. Valid values are one of: |
| pc (a program counter register. for example "name=eip;" (i386), |
| "name=rip;" (x86_64), "name=r15;" (32 bit arm) would |
| include a "generic=pc;" key value pair) |
| sp (a stack pointer register. for example "name=esp;" (i386), |
| "name=rsp;" (x86_64), "name=r13;" (32 bit arm) would |
| include a "generic=sp;" key value pair) |
| fp (a frame pointer register. for example "name=ebp;" (i386), |
| "name=rbp;" (x86_64), "name=r7;" (32 bit arm with macosx |
| ABI) would include a "generic=fp;" key value pair) |
| ra (a return address register. for example "name=lr;" (32 bit ARM) |
| would include a "generic=ra;" key value pair) |
| fp (a CPU flags register. for example "name=eflags;" (i386), |
| "name=rflags;" (x86_64), "name=cpsr;" (32 bit ARM) |
| would include a "generic=flags;" key value pair) |
| arg1 - arg8 (specified for registers that contain function |
| arguments when the argument fits into a register) |
| |
| //---------------------------------------------------------------------- |
| // "qHostInfo" |
| // |
| // BRIEF |
| // Get information about the host we are remotely connected to. |
| // |
| // PRIORITY TO IMPLEMENT |
| // High. This packet is usually very easy to implement and can help |
| // LLDB select the correct plug-ins for the job based on the target |
| // triple information that is suppied. |
| //---------------------------------------------------------------------- |
| |
| LLDB supports a host info call that gets all sorts of details of the system |
| that is being debugged: |
| |
| send packet: $qHostInfo#00 |
| read packet: $cputype:16777223;cpusubtype:3;ostype:darwin;vendor:apple;endian:little;ptrsize:8;#00 |
| |
| Key value pairs are one of: |
| |
| cputype: is a number that is the mach-o CPU type that is being debugged |
| cpusubtype: is a number that is the mach-o CPU subtype type that is being debugged |
| ostype: is a string the represents the OS being debugged (darwin, lunix, freebsd) |
| vendor: is a string that represents the vendor (apple) |
| endian: is one of "little", "big", or "pdp" |
| ptrsize: is a number that represents how big pointers are in bytes on the debug target |
| |
| //---------------------------------------------------------------------- |
| // "qShlibInfoAddr" |
| // |
| // BRIEF |
| // Get an address where the dynamic linker stores information about |
| // where shared libraries are loaded. |
| // |
| // PRIORITY TO IMPLEMENT |
| // High if you have a dynamic loader plug-in in LLDB for your target |
| // triple (see the "qHostInfo" packet) that can use this information. |
| // Many times address load randomization can make it hard to detect |
| // where the dynamic loader binary and data structures are located and |
| // some platforms know, or can find out where this information is. |
| // |
| // Low if you have a debug target where all object and symbol files |
| // contain static load addresses. |
| //---------------------------------------------------------------------- |
| |
| LLDB and GDB both support the "qShlibInfoAddr" packet which is a hint to each |
| debugger as to where to find the dynamic loader information. For darwin |
| binaires that run in user land this is the address of the "all_image_infos" |
| stucture in the "/usr/lib/dyld" executable, or the result of a TASK_DYLD_INFO |
| call. The result is returned as big endian hex bytes that are the address |
| value: |
| |
| send packet: $qShlibInfoAddr#00 |
| read packet: $7fff5fc40040#00 |
| |
| |
| |
| //---------------------------------------------------------------------- |
| // "qThreadStopInfo<tid>" |
| // |
| // BRIEF |
| // Get information about why a thread, whose ID is "<tid>", is stopped. |
| // |
| // PRIORITY TO IMPLEMENT |
| // High if you need to support multi-threaded or multi-core debugging. |
| // Many times one thread will hit a breakpoint and while the debugger |
| // is in the process of suspending the other threads, other threads |
| // will also hit a breakpoint. This packet allows LLDB to know why all |
| // threads (live system debug) / cores (JTAG) in your program have |
| // stopped and allows LLDB to display and control your program |
| // correctly. |
| //---------------------------------------------------------------------- |
| |
| LLDB tries to use the "qThreadStopInfo" packet which is formatted as |
| "qThreadStopInfo%x" where %x is the hex thread ID. This requests information |
| about why a thread is stopped. The response is the same as the stop reply |
| packets and tells us what happened to the other threads. The standard GDB |
| remote packets love to think that there is only _one_ reason that _one_ thread |
| stops at a time. This allows us to see why all threads stopped and allows us |
| to implement better multi-threaded debugging support. |
| |
| //---------------------------------------------------------------------- |
| // "QThreadSuffixSupported" |
| // |
| // BRIEF |
| // Try to enable thread suffix support for the 'g', 'G', 'p', and 'P' |
| // packets. |
| // |
| // PRIORITY TO IMPLEMENT |
| // High. Adding a thread suffix allows us to read and write registers |
| // more efficiently and stops us from having to select a thread with |
| // one packet and then read registers with a second packet. It also |
| // makes sure that no errors can occur where the debugger thinks it |
| // already has a thread selected (see the "Hg" packet from the standard |
| // GDB remote protocol documentation) yet the remote GDB server actually |
| // has another thread selected. |
| //---------------------------------------------------------------------- |
| |
| When reading thread registers, you currently need to set the current |
| thread,then read the registers. This is kind of cumbersome, so we added the |
| ability to query if the remote GDB server supports adding a "thread:<tid>;" |
| suffix to all packets that request information for a thread. To test if the |
| remote GDB server supports this feature: |
| |
| send packet: $QThreadSuffixSupported#00 |
| read packet: OK |
| |
| If "OK" is returned, then the 'g', 'G', 'p' and 'P' packets can accept a |
| thread suffix. So to send a 'g' packet (read all register values): |
| |
| send packet: $g;thread:<tid>;#00 |
| read packet: .... |
| |
| send packet: $G;thread:<tid>;#00 |
| read packet: .... |
| |
| send packet: $p1a;thread:<tid>;#00 |
| read packet: .... |
| |
| send packet: $P1a=1234abcd;thread:<tid>;#00 |
| read packet: .... |
| |
| |
| otherwise, without this you would need to always send two packets: |
| |
| send packet: $Hg<tid>#00 |
| read packet: .... |
| send packet: $g#00 |
| read packet: .... |
| |
| We also added support for allocating and deallocating memory. We use this to |
| allocate memory so we can run JITed code. |
| |
| //---------------------------------------------------------------------- |
| // "_M<size>,<permissions>" |
| // |
| // BRIEF |
| // Allocate memory on the remote target with the specified size and |
| // permissions. |
| // |
| // PRIORITY TO IMPLEMENT |
| // High if you want LLDB to be able to JIT code and run that code. JIT |
| // code also needs data which is also allocated and tracked. |
| // |
| // Low if you don't support running JIT'ed code. |
| //---------------------------------------------------------------------- |
| |
| The allocate memory packet starts with "_M<size>,<permissions>". It returns a |
| raw big endian address value, or "" for unimplemented, or "EXX" for an error |
| code. The packet is formatted as: |
| |
| char packet[256]; |
| int packet_len; |
| packet_len = ::snprintf ( |
| packet, |
| sizeof(packet), |
| "_M%zx,%s%s%s", |
| (size_t)size, |
| permissions & lldb::ePermissionsReadable ? "r" : "", |
| permissions & lldb::ePermissionsWritable ? "w" : "", |
| permissions & lldb::ePermissionsExecutable ? "x" : ""); |
| |
| You request a size and give the permissions. This packet does NOT need to be |
| implemented if you don't want to support running JITed code. The return value |
| is just the address of the newly allocated memory as raw big endian hex bytes. |
| |
| //---------------------------------------------------------------------- |
| // "_m<addr>" |
| // |
| // BRIEF |
| // Deallocate memory that was previously allocated using an allocate |
| // memory pack. |
| // |
| // PRIORITY TO IMPLEMENT |
| // High if you want LLDB to be able to JIT code and run that code. JIT |
| // code also needs data which is also allocated and tracked. |
| // |
| // Low if you don't support running JIT'ed code. |
| //---------------------------------------------------------------------- |
| |
| The deallocate memory packet is "_m<addr>" where you pass in the address you |
| got back from a previous call to the allocate memory packet. It returns "OK" |
| if the memory was successfully deallocated, or "EXX" for an error, or "" if |
| not supported. |
| |
| //---------------------------------------------------------------------- |
| // "qMemoryRegionInfo:<addr>" |
| // |
| // BRIEF |
| // Get information about the address the range that contains "<addr>" |
| // |
| // PRIORITY TO IMPLEMENT |
| // Medium. This is nice to have, but it isn't necessary. It helps LLDB |
| // do stack unwinding when we branch into memory that isn't executable. |
| // If we can detect that the code we are stopped in isn't executable, |
| // then we can recover registers for stack frames above the current |
| // frame. Otherwise we must assume we are in some JIT'ed code (not JIT |
| // code that LLDB has made) and assume that no registers are available |
| // in higher stack frames. |
| //---------------------------------------------------------------------- |
| |
| We added a way to get information for a memory region. The packet is: |
| |
| qMemoryRegionInfo:<addr> |
| |
| Where <addr> is a big endian hex address. The response is returned in a series |
| of tuples like the data returned in a stop reply packet. The currently valid |
| tuples tp return are: |
| |
| start:<start-addr>; // <start-addr> is a big endian hex address that is |
| // the start address of the range that contains <addr> |
| |
| size:<size>; // <size> is a big endian hex byte size of the address |
| // of the range that contains <addr> |
| |
| permissions:<permissions>; // <permissions> is a string that contains one |
| // or more of the characters from "rwx" |
| |
| error:<ascii-byte-error-string>; // where <ascii-byte-error-string> is |
| // a hex encoded string value that |
| // contains an error string |
| |
| If the address requested is not in a mapped region (e.g. we've jumped through |
| a NULL pointer and are at 0x0) currently lldb expects to get back the size |
| of the unmapped region -- that is, the distance to the next valid region. |
| For instance, with a Mac OS X process which has nothing mapped in the first |
| 4GB of its address space, if we're asking about address 0x2, |
| |
| qMemoryRegionInfo:2 |
| start:2;size:fffffffe; |
| |
| The lack of 'permissions:' indicates that none of read/write/execute are valid |
| for this region. |
| |
| //---------------------------------------------------------------------- |
| // Stop reply packet extensions |
| // |
| // BRIEF |
| // This section describes some of the additional information you can |
| // specify in stop reply packets that help LLDB to know more detailed |
| // information about your threads. |
| // |
| // DESCRIPTION |
| // Standard GDB remote stop reply packets are reply packets sent in |
| // response to a packet that made the program run. They come in the |
| // following forms: |
| // |
| // "SAA" |
| // "S" means signal and "AA" is a hex signal number that describes why |
| // the thread or stopped. It doesn't specify which thread, so the "T" |
| // packet is recommended to use instead of the "S" packet. |
| // |
| // "TAAkey1:value1;key2:value2;..." |
| // "T" means a thread stopped due to a unix signal where "AA" is a hex |
| // signal number that describes why the program stopped. This is |
| // followed by a series of key/value pairs: |
| // - If key is a hex number, it is a register number and value is |
| // the hex value of the register in debuggee endian byte order. |
| // - If key == "thread", then the value is the big endian hex |
| // thread-id of the stopped thread. |
| // - If key == "core", then value is a hex nujber of the core on |
| // which the stop was detected. |
| // - If key == "watch" or key == "rwatch" or key == "awatch", then |
| // value is the data address in big endian hex |
| // - If key == "library", then value is ignore and "qXfer:libraries:read" |
| // packets should be used to detect any newly loaded shared libraries |
| // |
| // "WAA" |
| // "W" means the process exited and "AA" is the exit status. |
| // |
| // "XAA" |
| // "X" means the process exited and "AA" is signal that caused the program |
| // to exit. |
| // |
| // "O<ascii-hex-string>" |
| // "O" means STDOUT has data that was written to its console and is |
| // being delivered to the debugger. This packet happens asynchronously |
| // and the debugger is expected to continue to way for another stop reply |
| // packet. |
| // |
| // LLDB EXTENSIONS |
| // |
| // We have extended the "T" packet to be able to also understand the |
| // following keys and values: |
| // |
| // KEY VALUE DESCRIPTION |
| // =========== ======== ================================================ |
| // "metype" unsigned mach exception type (the value of the EXC_XXX enumerations) |
| // as an unsigned integer. For targets with mach |
| // kernels only. |
| // |
| // "mecount" unsigned mach exception data count as an unsigned integer |
| // For targets with mach kernels only. |
| // |
| // "medata" unsigned There should be "mecount" of these and it is the data |
| // that goes along with a mach exception (as an unsigned |
| // integer). For targets with mach kernels only. |
| // |
| // "name" string The name of the thread as a plain string. The string |
| // must not contain an special packet characters or |
| // contain a ':' or a ';'. Use "hexname" if the thread |
| // name has special characters. |
| // |
| // "hexname" ascii-hex An ASCII hex string that contains the name of the thread |
| // |
| // "qaddr" hex Big endian hex value that contains the libdispatch |
| // queue address for the queue of the thread. |
| // |
| // "reason" enum The enumeration must be one of: |
| // "trace" the program stopped after a single instruction |
| // was executed on a core. Usually done when single |
| // stepping past a breakpoint |
| // "breakpoint" a breakpoint set using a 'z' packet was hit. |
| // "trap" stopped due to user interruption |
| // "signal" stopped due to an actual unix signal, not |
| // just the debugger using a unix signal to keep |
| // the GDB remote client happy. |
| // "watchpoint". Should be used in conjunction with |
| // the "watch"/"rwatch"/"awatch" key value pairs. |
| // "exception" an exception stop reason. Use with |
| // the "description" key/value pair to describe the |
| // exceptional event the user should see as the stop |
| // reason. |
| // "description" ascii-hex An ASCII hex string that contains a more descriptive |
| // reason that the thread stopped. This is only needed |
| // if none of the key/value pairs are enough to |
| // describe why something stopped. |
| // |
| // BEST PRACTICES: |
| // Since register values can be supplied with this packet, it is often useful |
| // to return the PC, SP, FP, LR (if any), and FLAGS regsiters so that separate |
| // packets don't need to be sent to read each of these registers from each |
| // thread. |
| // |
| // If a thread is stopped for no reason (like just because another thread |
| // stopped, or because when one core stops all cores should stop), use a |
| // "T" packet with "00" as the signal number and fill in as many key values |
| // and registers as possible. |
| // |
| // LLDB likes to know why a thread stopped since many thread contol |
| // operations like stepping over a source line, actually are implemented |
| // by running the process multiple times. If a breakpoint is hit while |
| // trying to step over a source line and LLDB finds out that a breakpoint |
| // is hit in the "reason", we will know to stop trying to do the step |
| // over because something happened that should stop us from trying to |
| // do the step. If we are at a breakpoint and we disable the breakpoint |
| // at the current PC and do an instruction single step, knowing that |
| // we stopped due to a "trace" helps us know that we can continue |
| // running versus stopping due to a "breakpoint" (if we have two |
| // breakpoint instruction on consucutive instructions). So the more info |
| // we can get about the reason a thread stops, the better job LLDB can |
| // do when controlling your process. A typical GDB server behavior is |
| // to send a SIGTRAP for breakpoints _and_ also when instruction single |
| // stepping, in this case the debugger doesn't really know why we |
| // stopped and it can make it hard for the debugger to control your |
| // program correctly. What if a real SIGTRAP was delivered to a thread |
| // while we were trying to single step? We woudn't know the difference |
| // with a standard GDB remote server and we could do the wrong thing. |
| // |
| // PRIORITY TO IMPLEMENT |
| // High. Having the extra information in your stop reply packets makes |
| // your debug session more reliable and informative. |
| //---------------------------------------------------------------------- |
| |