Index: vendor/lldb/dist/docs/lldb-gdb-remote.txt =================================================================== --- vendor/lldb/dist/docs/lldb-gdb-remote.txt (revision 319149) +++ vendor/lldb/dist/docs/lldb-gdb-remote.txt (revision 319150) @@ -1,1731 +1,1888 @@ 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 minimize 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 start 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. // // NB: key/value pairs are sent as-is so gdb-remote protocol meta characters // (e.g. '#' or '$') are not acceptable. If any non-printable or // metacharacters are present in the strings, QEnvironmentHexEncoded // should be used instead if it is available. If you don't want to // scan the environment strings before sending, prefer // the QEnvironmentHexEncoded packet over QEnvironment, if it is // available. // // 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. //---------------------------------------------------------------------- // "QEnvironmentHexEncoded:HEX-ENCODING(NAME=VALUE)" // // BRIEF // Setup the environment up for a new child process that will soon be // launched using the "A" packet. // // The only difference between this packet and QEnvironment is that the // environment key-value pair is ascii hex encoded for transmission. // This allows values with gdb-remote metacharacters like '#' to be sent. // // 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:41434b5f434f4c4f525f46494c454e414d453d626f6c642379656c6c6f77#00 read packet: $OK#00 This packet can be sent one or more times _prior_ to sending a "A" packet. //---------------------------------------------------------------------- // "QSetSTDIN:" // "QSetSTDOUT:" // "QSetSTDERR:" // // 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: QSetSTDOUT: QSetSTDERR: These packets must be sent _prior_ to sending a "A" packet. //---------------------------------------------------------------------- // "QSetWorkingDir:" // // 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: This packet must be sent _prior_ to sending a "A" packet. //---------------------------------------------------------------------- // "QSetDisableASLR:" // // 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. //---------------------------------------------------------------------- // QListThreadsInStopReply // // BRIEF // Enable the threads: and thread-pcs: data in the question-mark packet // ("T packet") responses when the stub reports that a program has // stopped executing. // // PRIORITY TO IMPLEMENT // Performance. This is a performance benefit to lldb if the thread id's // and thread pc values are provided to lldb in the T stop packet -- if // they are not provided to lldb, lldb will likely need to send one to // two packets per thread to fetch the data at every private stop. //---------------------------------------------------------------------- send packet: QListThreadsInStopReply read packet: OK //---------------------------------------------------------------------- +// jTraceStart: +// +// BRIEF +// Packet for starting trace of type lldb::TraceType. The following +// parameters should be appended to the packet formatted as a JSON +// dictionary, where the schematic for the JSON dictionary in terms of +// the recognized Keys is given below in the table. +// Different tracing types could require different custom parameters. +// Such custom tracing parameters if needed should be collectively +// specified in a JSON dictionary and the dictionary can be appended +// to this packet (as Value corresponding to "params"). Since sending +// JSON data over gdb-remote protocol has certain limitations, binary +// escaping convention should be used. +// +// Following is the list of parameters - +// +// Key Value (Integer) (O)Optional/ +// (except params which should be a (M)Mandatory +// JSON dictionary) +// ========== ==================================================== +// +// type The type of trace to start (see M +// lldb-enumerations for TraceType) +// +// buffersize The size of the buffer to allocate M +// for trace gathering. +// +// threadid The id of the thread to start tracing O +// on. +// +// metabuffersize The size of buffer to hold meta data O +// used for decoding the trace data. +// +// params Any parameters that are specific to O +// certain trace technologies should be +// collectively specified as a JSON +// dictionary +// ========== ==================================================== +// +// Each tracing instance is identified by a trace id which is returned +// as the reply to this packet. In case the tracing failed to begin an +// error code is returned instead. +//---------------------------------------------------------------------- + +send packet: jTraceStart:{"type":,"buffersize":}] +read packet: /E + +//---------------------------------------------------------------------- +// jTraceStop: +// +// BRIEF +// Stop tracing instance with trace id , of course trace +// needs to be started before. The following parameters should be +// formatted as a JSON dictionary to the packet. Since sending +// JSON data over gdb-remote protocol has certain limitations, binary +// escaping convention should be used. +// +// Following is the list of parameters - +// +// Key Value (Integer) (O)Optional/ +// (M)Mandatory +// ========== ==================================================== +// +// traceid The trace id of the tracing instance M +// +// threadid The id of the thread to stop tracing O +// on. Since could map to +// multiple trace instances (in case it +// maps to the complete process), the +// threadid of a particular thread could +// be appended as "threadid:;" +// to stop tracing on that thread. +// ========== ==================================================== +// +// An OK response is sent in case of success else an error code is +// returned. +//---------------------------------------------------------------------- + +send packet: jTraceStop:{"traceid":}] +read packet: /E + +//---------------------------------------------------------------------- +// jTraceBufferRead: +// +// BRIEF +// Packet for reading the trace for tracing instance , i.e the +// id obtained from StartTrace API. The following parameters should be +// formatted as a JSON dictionary to the packet. Since sending +// JSON data over gdb-remote protocol has certain limitations, binary +// escaping convention should be used. +// +// Following is the list of parameters - +// +// Key Value (Integer) (O)Optional/ +// (M)Mandatory +// ========== ==================================================== +// traceid The trace id of the tracing instance M +// +// offset The offset to start reading the data M +// from. +// +// buffersize The size of the data intended to read. M +// +// threadid The id of the thread to retrieve data O +// from. +// ========== ==================================================== +// +// The trace data is sent as raw binary data if the read was successful +// else an error code is sent. +//---------------------------------------------------------------------- + +send packet: jTraceBufferRead:{"traceid":,"offset":,"buffersize":}] +read packet: /E + +//---------------------------------------------------------------------- +// jTraceMetaRead: +// +// BRIEF +// Similar Packet as above except it reads meta data. +//---------------------------------------------------------------------- + +/---------------------------------------------------------------------- +// jTraceConfigRead: +// +// BRIEF +// Request the trace configuration for the tracing instance with id +// . +// +// Following is the list of parameters - +// +// Key Value (Integer) (O)Optional/ +// (M)Mandatory +// ========== ==================================================== +// traceid The trace id of the tracing instance M +// +// threadid The id of the thread to obtain trace O +// configuration from. Since +// could map to multiple trace instances +// (in case it maps to the complete +// process), the threadid of a particular +// thread could be appended as +// "threadid:;" to obtain the +// trace configuration of that thread. +// ========== ==================================================== +// +// In the response packet the trace configuration is sent as text, +// formatted as a JSON dictionary. Since sending JSON data over +// gdb-remote protocol has certain limitations, binary escaping +// convention is used. +// In case the trace instance with the was not found, an +// error code is returned. +//---------------------------------------------------------------------- + +send packet: jTraceConfigRead:{"traceid":} +read packet: {"conf1":,"conf2":,"params":{"paramName":paramValue}]}];/E + +//---------------------------------------------------------------------- // "qRegisterInfo" // // 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. // // NB: As of summer 2015, lldb can get register information from the // "qXfer:features:read:target.xml" FSF gdb standard register packet // where the stub provides register definitions in an XML file. // If qXfer:features:read:target.xml is supported, qRegisterInfo does // not need to be implemented. //---------------------------------------------------------------------- With LLDB, for register information, remote GDB servers can add support for the "qRegisterInfoN" packet where "N" is a zero based base16 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 series 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 offset: fields should not leave a gap anywhere in the g/G packet -- the register values should be appended one after another. For instance, if the register context for a thread looks like struct rctx { uint32_t gpr1; // offset 0 uint32_t gpr2; // offset 4 uint32_t gpr3; // offset 8 uint64_t fp1; // offset 16 }; You may end up with a 4-byte gap between gpr3 and fp1 on architectures that align values like this. The correct offset: value for fp1 is 12 - in the g/G packet fp1 will immediately follow gpr3, even though the in-memory thread structure has an empty 4 bytes for alignment between these two registers. 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). Base 10. offset The offset within the "g" and "G" packet of the register data for this register. This is the byte offset once the data has been transformed into binary, not the character offset into the g/G packet. Base 10. encoding The encoding type of the register which must be one of: uint (unsigned integer) sint (signed integer) ieee754 (IEEE 754 float) vector (vector register) 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 register 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). The supplied number will be decoded like a string passed to strtoul() with a base of zero, so the number can be decimal, or hex if it is prefixed with "0x". 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. The supplied number will be decoded like a string passed to strtoul() with a base of zero, so the number can be decimal, or hex if it is prefixed with "0x". 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) container-regs The value for this key is a comma separated list of raw hex (optional leading "0x") register numbers. This specifies that this register is contained in other concrete register values. For example "eax" is in the lower 32 bits of the "rax" register value for x86_64, so "eax" could specify that it is contained in "rax" by specifying the register number for "rax" (whose register number is 0x00) "container-regs:00;" If a register is comprised of one or more registers, like "d0" is ARM which is a 64 bit register, it might be made up of "s0" and "s1". If the register number for "s0" is 0x20, and the register number of "s1" is "0x21", the "container-regs" key/value pair would be: "container-regs:20,21;" This is handy for defining what GDB used to call "pseudo" registers. These registers are never requested by LLDB via the register read or write packets, the container registers will be requested on behalf of this register. invalidate-regs The value for this key is a comma separated list of raw hex (optional leading "0x") register numbers. This specifies which register values should be invalidated when this register is modified. For example if modifying "eax" would cause "rax", "eax", "ax", "ah", and "al" to be modified where rax is 0x0, eax is 0x15, ax is 0x25, ah is 0x35, and al is 0x39, the "invalidate-regs" key/value pair would be: "invalidate-regs:0,15,25,35,39;" If there is a single register that gets invalidated, then omit the comma and just list a single register: "invalidate-regs:0;" This is handy when modifying a specific register can cause other register values to change. For example, when debugging an ARM target, modifying the CPSR register can cause the r8 - r14 and cpsr value to change depending on if the mode has changed. //---------------------------------------------------------------------- // "qPlatform_shell" // // BRIEF // Run a command in a shell on the connected remote machine. // // PRIORITY TO IMPLEMENT // High. This command allows LLDB clients to run arbitrary shell // commands on a remote host. // /---------------------------------------------------------------------- The request consists of the command to be executed encoded in ASCII characters converted into hex bytes. The response to this packet consists of the letter F followed by the return code, followed by the signal number (or 0 if no signal was delivered), and escaped bytes of captured program output. Below is an example communication from a client sending an "ls -la" command: send packet: $qPlatform_shell:6c73202d6c61,00000002#ec read packet: $F,00000000,00000000,total 4736 drwxrwxr-x 16 username groupname 4096 Aug 15 21:36 . drwxr-xr-x 17 username groupname 4096 Aug 10 16:39 .. -rw-rw-r-- 1 username groupname 73875 Aug 12 16:46 notes.txt drwxrwxr-x 5 username groupname 4096 Aug 15 21:36 source.cpp -rw-r--r-- 1 username groupname 2792 Aug 12 16:46 a.out -rw-r--r-- 1 username groupname 3190 Aug 12 16:46 Makefile //---------------------------------------------------------------------- // "qPlatform_mkdir" // // BRIEF // Creates a new directory on the connected remote machine. // // PRIORITY TO IMPLEMENT // Low. This command allows LLDB clients to create new directories on // a remote host. // /---------------------------------------------------------------------- Request: qPlatform_mkdir:, Reply: F mkdir called successfully and returned with the given return code Exx An error occurred //---------------------------------------------------------------------- // "qPlatform_chmod" // // BRIEF // Change the permissions of a file on the connected remote machine. // // PRIORITY TO IMPLEMENT // Low. This command allows LLDB clients to change the permissions of // a file on the remote host. // /---------------------------------------------------------------------- Request: qPlatform_chmod:, Reply: F chmod called successfully and returned with the given return code Exx An error occurred //---------------------------------------------------------------------- // "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 supplied. //---------------------------------------------------------------------- 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 (base 10) cpusubtype: is a number that is the mach-o CPU subtype type that is being debugged (base 10) triple: a string for the target triple (x86_64-apple-macosx) that can be used to specify arch + vendor + os in one entry vendor: a string for the vendor (apple), not needed if "triple" is specified ostype: a string for the OS being debugged (macosx, linux, freebsd, ios, watchos), not needed if "triple" is specified endian: is one of "little", "big", or "pdp" ptrsize: an unsigned number that represents how big pointers are in bytes on the debug target hostname: the hostname of the host that is running the GDB server if available os_build: a string for the OS build for the remote host as a string value os_kernel: a string describing the kernel version os_version: a version string that represents the current OS version (10.8.2) watchpoint_exceptions_received: one of "before" or "after" to specify if a watchpoint is triggered before or after the pc when it stops default_packet_timeout: an unsigned number that specifies the default timeout in seconds distribution_id: optional. For linux, specifies distribution id (e.g. ubuntu, fedora, etc.) osmajor: optional, specifies the major version number of the OS (e.g. for Mac OS X 10.11.2, it would be 10) osminor: optional, specifies the minor version number of the OS (e.g. for Mac OS X 10.11.2, it would be 11) ospatch: optional, specifies the patch level number of the OS (e.g. for Mac OS X 10.11.2, it would be 2) //---------------------------------------------------------------------- // "qGDBServerVersion" // // BRIEF // Get version information about this implementation of the gdb-remote // protocol. // // PRIORITY TO IMPLEMENT // High. This packet is usually very easy to implement and can help // LLDB to work around bugs in a server's implementation when they // are found. //---------------------------------------------------------------------- The goal of this packet is to provide enough information about an implementation of the gdb-remote-protocol server that lldb can work around implementation problems that are discovered after the version has been released/deployed. The name and version number should be sufficiently unique that lldb can unambiguously identify the origin of the program (for instance, debugserver from lldb) and the version/submission number/patch level of the program - whatever is appropriate for your server implementation. The packet follows the key-value pair model, semicolon separated. send packet: $qGDBServerVersion#00 read packet: $name:debugserver;version:310.2;#00 Other clients may find other key-value pairs to be useful for identifying a gdb stub. Patch level, release name, build number may all be keys that better describe your implementation's version. Suggested key names: name : the name of your remote server - "debugserver" is the lldb standard implementation version : identifies the version number of this server patch_level : the patch level of this server release_name : the name of this release, if your project uses names build_number : if you use a build system with increasing build numbers, this may be the right key name for your server major_version : major version number minor_version : minor version number //---------------------------------------------------------------------- // "qProcessInfo" // // BRIEF // Get information about the process we are currently debugging. // // PRIORITY TO IMPLEMENT // Medium. On systems which can launch multiple different architecture processes, // the qHostInfo may not disambiguate sufficiently to know what kind of // process is being debugged. // e.g. on a 64-bit x86 Mac system both 32-bit and 64-bit user processes are possible, // and with Mach-O universal files, the executable file may contain both 32- and // 64-bit slices so it may be impossible to know until you're attached to a real // process to know what you're working with. // // All numeric fields return base-16 numbers without any "0x" prefix. //---------------------------------------------------------------------- An i386 process: send packet: $qProcessInfo#00 read packet: $pid:42a8;parent-pid:42bf;real-uid:ecf;real-gid:b;effective-uid:ecf;effective-gid:b;cputype:7;cpusubtype:3;ostype:macosx;vendor:apple;endian:little;ptrsize:4;#00 An x86_64 process: send packet: $qProcessInfo#00 read packet: $pid:d22c;parent-pid:d34d;real-uid:ecf;real-gid:b;effective-uid:ecf;effective-gid:b;cputype:1000007;cpusubtype:3;ostype:macosx;vendor:apple;endian:little;ptrsize:8;#00 Key value pairs include: pid: the process id parent-pid: the process of the parent process (often debugserver will become the parent when attaching) real-uid: the real user id of the process real-gid: the real group id of the process effective-uid: the effective user id of the process effective-gid: the effective group id of the process cputype: the Mach-O CPU type of the process (base 16) cpusubtype: the Mach-O CPU subtype of the process (base 16) ostype: is a string the represents the OS being debugged (darwin, linux, 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 //---------------------------------------------------------------------- // "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 binaries that run in user land this is the address of the "all_image_infos" structure 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" // // BRIEF // Get information about why a thread, whose ID is "", 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:;" 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:;#00 read packet: .... send packet: $G;thread:;#00 read packet: .... send packet: $p1a;thread:;#00 read packet: .... send packet: $P1a=1234abcd;thread:;#00 read packet: .... otherwise, without this you would need to always send two packets: send packet: $Hg#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," // // 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,". 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" // // 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" 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:" // // BRIEF // Get information about the address range that contains "" // // 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: Where 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 to return are: start:; // is a big endian hex address that is // the start address of the range that contains size:; // is a big endian hex byte size of the address // of the range that contains permissions:; // is a string that contains one // or more of the characters from "rwx" name:; // is a hex encoded string that contains the name of // the memory region mapped at the given address. In case of // regions backed by a file it have to be the absolute path of // the file while for anonymous regions it have to be the name // associated to the region if that is available. error:; // where 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. //---------------------------------------------------------------------- // "x" - Binary memory read // // Like the 'm' (read) and 'M' (write) packets, this is a partner to the // 'X' (write binary data) packet, 'x'. // // It is called like // // xADDRESS,LENGTH // // where both ADDRESS and LENGTH are big-endian base 16 values. // // To test if this packet is available, send a addr/len of 0: // // x0,0 // // and you will get an "OK" response. // // The reply will be the data requested in 8-bit binary data format. // The standard quoting is applied to the payload -- characters // } # $ * // will all be escaped with '}' (0x7d) character and then XOR'ed with 0x20. // // A typical use to read 512 bytes at 0x1000 would look like // // x0x1000,0x200 // // The "0x" prefixes are optional - like most of the gdb-remote packets, // omitting them will work fine; these numbers are always base 16. // // The length of the payload is not provided. A reliable, 8-bit clean, // transport layer is assumed. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // Detach and stay stopped: // // We extended the "D" packet to specify that the monitor should keep the // target suspended on detach. The normal behavior is to resume execution // on detach. We will send: // // qSupportsDetachAndStayStopped: // // to query whether the monitor supports the extended detach, and if it does, // when we want the monitor to detach but not resume the target, we will // send: // // D1 // // In any case, if we want the normal detach behavior we will just send: // // D //---------------------------------------------------------------------- //---------------------------------------------------------------------- // QSaveRegisterState // QSaveRegisterState;thread:XXXX; // // BRIEF // The QSaveRegisterState packet tells the remote debugserver to save // all registers and return a non-zero unique integer ID that // represents these save registers. If thread suffixes are enabled the // second form of this packet is used, otherwise the first form is // used. This packet is called prior to executing an expression, so // the remote GDB server should do anything it needs to in order to // ensure the registers that are saved are correct. On MacOSX this // involves calling "thread_abort_safely(mach_port_t thread)" to // ensure we get the correct registers for a thread in case it is // currently having code run on its behalf in the kernel. // // RESPONSE // unsigned - The save_id result is a non-zero unsigned integer value // that can be passed back to the GDB server using a // QRestoreRegisterState packet to restore the registers // one time. // "EXX" - or an error code in the form of EXX where XX is a // hex error code. // // PRIORITY TO IMPLEMENT // Low, this is mostly a convenience packet to avoid having to send all // registers via a g packet. It should only be implemented if support // for the QRestoreRegisterState is added. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // QRestoreRegisterState: // QRestoreRegisterState:;thread:XXXX; // // BRIEF // The QRestoreRegisterState packet tells the remote debugserver to // restore all registers using the "save_id" which is an unsigned // integer that was returned from a previous call to // QSaveRegisterState. The restoration process can only be done once // as the data backing the register state will be freed upon the // completion of the QRestoreRegisterState command. // // If thread suffixes are enabled the second form of this packet is // used, otherwise the first form is used. // // RESPONSE // "OK" - if all registers were successfully restored // "EXX" - for any errors // // PRIORITY TO IMPLEMENT // Low, this is mostly a convenience packet to avoid having to send all // registers via a g packet. It should only be implemented if support // for the QSaveRegisterState is added. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // qFileLoadAddress: // // BRIEF // Get the load address of a memory mapped file. // The load address is defined as the address of the first memory // region what contains data mapped from the specified file. // // RESPONSE // - Load address of the file in big endian encoding // "E01" - the requested file isn't loaded // "EXX" - for any other errors // // PRIORITY TO IMPLEMENT // Low, required if dynamic linker don't fill in the load address of // some object file in the rendezvous data structure. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // qModuleInfo:; // // BRIEF // Get information for a module by given module path and architecture. // // RESPONSE // "(uuid|md5):...;triple:...;file_offset:...;file_size...;" // "EXX" - for any errors // // PRIORITY TO IMPLEMENT // Optional, required if dynamic loader cannot fetch module's information like // UUID directly from inferior's memory. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // jModulesInfo:[{"file":"...",triple:"..."}, ...] // // BRIEF // Get information for a list of modules by given module path and // architecture. // // RESPONSE // A JSON array of dictionaries containing the following keys: uuid, // triple, file_path, file_offset, file_size. The meaning of the fields // is the same as in the qModuleInfo packet. The server signals the // failure to retrieve the module info for a file by ommiting the // corresponding array entry from the response. The server may also // include entries the client did not ask for, if it has reason to // the modules will be interesting to the client. // // PRIORITY TO IMPLEMENT // Optional. If not implemented, qModuleInfo packet will be used, which // may be slower if the target contains a large number of modules and // the communication link has a non-negligible latency. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // 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 number 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" // "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 wait 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. // // "threads" comma-sep-base16 A list of thread ids for all threads (including // the thread that we're reporting as stopped) that // are live in the process right now. lldb may // request that this be included in the T packet via // the QListThreadsInStopReply packet earlier in // the debug session. // // Example: // threads:63387,633b2,63424,63462,63486; // // "thread-pcs" comma-sep-base16 A list of pc values for all threads that currently // exist in the process, including the thread that // this T packet is reporting as stopped. // This key-value pair will only be emitted when the // "threads" key is already included in the T packet. // The pc values correspond to the threads reported // in the "threads" list. The number of pcs in the // "thread-pcs" list will be the same as the number of // threads in the "threads" list. // lldb may request that this be included in the T // packet via the QListThreadsInStopReply packet // earlier in the debug session. // // Example: // thread-pcs:dec14,2cf872b0,2cf8681c,2d02d68c,2cf716a8; // // 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 registers 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 control // 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 consecutive 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 wouldn'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. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // PLATFORM EXTENSION - for use as a GDB remote platform //---------------------------------------------------------------------- // "qfProcessInfo" // "qsProcessInfo" // // BRIEF // Get the first process info (qfProcessInfo) or subsequent process // info (qsProcessInfo) for one or more processes on the remote // platform. The first call gets the first match and subsequent calls // to qsProcessInfo gets the subsequent matches. Return an error EXX, // where XX are two hex digits, when no more matches are available. // // PRIORITY TO IMPLEMENT // Required. The qfProcessInfo packet can be followed by a ':' and // some key value pairs. The key value pairs in the command are: // // KEY VALUE DESCRIPTION // =========== ======== ================================================ // "name" ascii-hex An ASCII hex string that contains the name of // the process that will be matched. // "name_match" enum One of: "equals", "starts_with", "ends_with", // "contains" or "regex" // "pid" integer A string value containing the decimal process ID // "parent_pid" integer A string value containing the decimal parent // process ID // "uid" integer A string value containing the decimal user ID // "gid" integer A string value containing the decimal group ID // "euid" integer A string value containing the decimal effective user ID // "egid" integer A string value containing the decimal effective group ID // "all_users" bool A boolean value that specifies if processes should // be listed for all users, not just the user that the // platform is running as // "triple" string An ASCII triple string ("x86_64", // "x86_64-apple-macosx", "armv7-apple-ios") // // The response consists of key/value pairs where the key is separated from the // values with colons and each pair is terminated with a semi colon. For a list // of the key/value pairs in the response see the "qProcessInfoPID" packet // documentation. // // Sample packet/response: // send packet: $qfProcessInfo#00 // read packet: $pid:60001;ppid:59948;uid:7746;gid:11;euid:7746;egid:11;name:6c6c6462;triple:x86_64-apple-macosx;#00 // send packet: $qsProcessInfo#00 // read packet: $pid:59992;ppid:192;uid:7746;gid:11;euid:7746;egid:11;name:6d64776f726b6572;triple:x86_64-apple-macosx;#00 // send packet: $qsProcessInfo#00 // read packet: $E04#00 //---------------------------------------------------------------------- //---------------------------------------------------------------------- // PLATFORM EXTENSION - for use as a GDB remote platform //---------------------------------------------------------------------- // "qLaunchGDBServer" // // BRIEF // Have the remote platform launch a GDB server. // // PRIORITY TO IMPLEMENT // Required. The qLaunchGDBServer packet must be followed by a ':' and // some key value pairs. The key value pairs in the command are: // // KEY VALUE DESCRIPTION // =========== ======== ================================================ // "port" integer A string value containing the decimal port ID or // zero if the port should be bound and returned // // "host" integer The host that connections should be limited to // when the GDB server is connected to. // // The response consists of key/value pairs where the key is separated from the // values with colons and each pair is terminated with a semi colon. // // Sample packet/response: // send packet: $qLaunchGDBServer:port:0;host:lldb.apple.com;#00 // read packet: $pid:60025;port:50776;#00 // // The "pid" key/value pair is only specified if the remote platform launched // a separate process for the GDB remote server and can be omitted if no // process was separately launched. // // The "port" key/value pair in the response lets clients know what port number // to attach to in case zero was specified as the "port" in the sent command. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // PLATFORM EXTENSION - for use as a GDB remote platform //---------------------------------------------------------------------- // "qProcessInfoPID:PID" // // BRIEF // Have the remote platform get detailed information on a process by // ID. PID is specified as a decimal integer. // // PRIORITY TO IMPLEMENT // Optional. // // The response consists of key/value pairs where the key is separated from the // values with colons and each pair is terminated with a semi colon. // // The key value pairs in the response are: // // KEY VALUE DESCRIPTION // =========== ======== ================================================ // "pid" integer Process ID as a decimal integer string // "ppid" integer Parent process ID as a decimal integer string // "uid" integer A string value containing the decimal user ID // "gid" integer A string value containing the decimal group ID // "euid" integer A string value containing the decimal effective user ID // "egid" integer A string value containing the decimal effective group ID // "name" ascii-hex An ASCII hex string that contains the name of the process // "triple" string A target triple ("x86_64-apple-macosx", "armv7-apple-ios") // // Sample packet/response: // send packet: $qProcessInfoPID:60050#00 // read packet: $pid:60050;ppid:59948;uid:7746;gid:11;euid:7746;egid:11;name:6c6c6462;triple:x86_64-apple-macosx;#00 //---------------------------------------------------------------------- //---------------------------------------------------------------------- // "vAttachName" // // BRIEF // Same as vAttach, except instead of a "pid" you send a process name. // // PRIORITY TO IMPLEMENT // Low. Only needed for "process attach -n". If the packet isn't supported // then "process attach -n" will fail gracefully. So you need only to support // it if attaching to a process by name makes sense for your environment. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // "vAttachWait" // // BRIEF // Same as vAttachName, except that the stub should wait for the next instance // of a process by that name to be launched and attach to that. // // PRIORITY TO IMPLEMENT // Low. Only needed to support "process attach -w -n" which will fail // gracefully if the packet is not supported. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // "qAttachOrWaitSupported" // // BRIEF // This is a binary "is it supported" query. Return OK if you support // vAttachOrWait // // PRIORITY TO IMPLEMENT // Low. This is required if you support vAttachOrWait, otherwise no support // is needed since the standard "I don't recognize this packet" response // will do the right thing. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // "vAttachOrWait" // // BRIEF // Same as vAttachWait, except that the stub will attach to a process // by name if it exists, and if it does not, it will wait for a process // of that name to appear and attach to it. // // PRIORITY TO IMPLEMENT // Low. Only needed to implement "process attach -w -i false -n". If // you don't implement it but do implement -n AND lldb can somehow get // a process list from your device, it will fall back on scanning the // process list, and sending vAttach or vAttachWait depending on // whether the requested process exists already. This is racy, // however, so if you want to support this behavior it is better to // support this packet. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // "jThreadExtendedInfo" // // BRIEF // This packet, which takes its arguments as JSON and sends its reply as // JSON, allows the gdb remote stub to provide additional information // about a given thread. // // PRIORITY TO IMPLEMENT // Low. This packet is only needed if the gdb remote stub wants to // provide interesting additional information about a thread for the // user. // // This packet takes its arguments in JSON form ( http://www.json.org ). // At a minimum, a thread must be specified, for example: // // jThreadExtendedInfo:{"thread":612910} // // Because this is a JSON string, the thread number is provided in base10. // Additional key-value pairs may be provided by lldb to the gdb remote // stub. For instance, on some versions of Mac OS X, lldb can read offset // information out of the system libraries. Using those offsets, debugserver // is able to find the Thread Specific Address (TSD) for a thread and include // that in the return information. So lldb will send these additional fields // like so: // // jThreadExtendedInfo:{"plo_pthread_tsd_base_address_offset":0,"plo_pthread_tsd_base_offset":224,"plo_pthread_tsd_entry_size":8,"thread":612910} // // There are no requirements for what is included in the response. A simple // reply on a Mac OS X Yosemite / iOS 8 may include the pthread_t value, the // Thread Specific Data (TSD) address, the dispatch_queue_t value if the thread // is associated with a GCD queue, and the requested Quality of Service (QoS) // information about that thread. For instance, a reply may look like: // // {"tsd_address":4371349728,"requested_qos":{"enum_value":33,"constant_name":"QOS_CLASS_USER_INTERACTIVE","printable_name":"User Interactive"},"pthread_t":4371349504,"dispatch_queue_t":140735087127872} // // tsd_address, pthread_t, and dispatch_queue_t are all simple key-value pairs. // The JSON standard requires that numbers be expressed in base 10 - so all of // these are. requested_qos is a dictionary with three key-value pairs in it - // so the UI layer may choose the form most appropriate for displaying to the user. // // Sending JSON over gdb-remote protocol introduces some problems. We may be // sending strings with arbitrary contents in them, including the '#', '$', and '*' // characters that have special meaning in gdb-remote protocol and cannot occur // in the middle of the string. The standard solution for this would be to require // ascii-hex encoding of all strings, or ascii-hex encode the entire JSON payload. // // Instead, the binary escaping convention is used for JSON data. This convention // (e.g. used for the X packet) says that if '#', '$', '*', or '}' are to occur in // the payload, the character '}' (0x7d) is emitted, then the metacharacter is emitted // xor'ed by 0x20. The '}' character occurs in every JSON payload at least once, and // '}' ^ 0x20 happens to be ']' so the raw packet characters for a request will look // like // // jThreadExtendedInfo:{"thread":612910}] // // on the wire. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // "QEnableCompression" // // BRIEF // This packet enables compression of the packets that the debug stub sends to lldb. // If the debug stub can support compression, it indictes this in the reply of the // "qSupported" packet. e.g. // LLDB SENDS: qSupported:xmlRegisters=i386,arm,mips // STUB REPLIES: qXfer:features:read+;SupportedCompressions=lzfse,zlib-deflate,lz4,lzma;DefaultCompressionMinSize=384 // // If lldb knows how to use any of these compression algorithms, it can ask that this // compression mode be enabled. It may optionally change the minimum packet size // where compression is used. Typically small packets do not benefit from compression, // as well as compression headers -- compression is most beneficial with larger packets. // // QEnableCompression:type:zlib-deflate; // or // QEnableCompression:type:zlib-deflate;minsize:512; // // The debug stub should reply with an uncompressed "OK" packet to indicate that the // request was accepted. All further packets the stub sends will use this compression. // // Packets are compressed as the last step before they are sent from the stub, and // decompressed as the first step after they are received. The packet format in compressed // mode becomes one of two: // // $N#00 // // $C:#00 // // Where "#00" is the actual checksum value if noack mode is not enabled. The checksum // value is for the "N" or // "C:" bytes in the packet. // // The size of the uncompressed payload in base10 is provided because it will simplify // decompression if the final buffer size needed is known ahead of time. // // Compression on low-latency connections is unlikely to be an improvement. Particularly // when the debug stub and lldb are running on the same host. It should only be used // for slow connections, and likely only for larger packets. // // Example compression algorithsm that may be used include // // zlib-deflate // The raw DEFLATE format as described in IETF RFC 1951. With the ZLIB library, you // can compress to this format with an initialization like // deflateInit2 (&stream, 5, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) // and you can decompress with an initialization like // inflateInit2 (&stream, -15) // // lz4 // https://en.wikipedia.org/wiki/LZ4_(compression_algorithm) // https://github.com/Cyan4973/lz4 // The libcompression APIs on darwin systems call this COMPRESSION_LZ4_RAW. // // lzfse // An Apple proprietary compression algorithm implemented in libcompression. // // lzma // libcompression implements "LZMA level 6", the default compression for the // open source LZMA implementation. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // "jGetLoadedDynamicLibrariesInfos" // // BRIEF // This packet asks the remote debug stub to send the details about libraries // being added/removed from the process as a performance optimization. // // There are three ways this packet can be used. All three return a dictionary of // binary images formatted the same way. // // On MacOS X 10.11, iOS 9, tvOS 9, watchOS 2 and earlier, the packet is used like // jGetLoadedDynamicLibrariesInfos:{"image_count":1,"image_list_address":140734800075128} // where the image_list_address is an array of {void* load_addr, void* mod_date, void* pathname} // in the inferior process memory (and image_count is the number of elements in this array). // lldb is using information from the dyld_all_image_infos structure to make these requests to // debugserver. This use is not supported on macOS 10.12, iOS 10, tvOS 10, watchOS 3 or newer. // // On macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer, there are two calls. One requests information // on all shared libraries: // jGetLoadedDynamicLibrariesInfos:{"fetch_all_solibs":true} // And the second requests information about a list of shared libraries, given their load addresses: // jGetLoadedDynamicLibrariesInfos:{"solib_addresses":[8382824135,3258302053,830202858503]} // // The second call is both a performance optimization (instead of having lldb read the mach-o header/load commands // out of memory with generic read packets) but also adds additional information in the form of the // filename of the shared libraries (which is not available in the mach-o header/load commands.) // // An example using the Mac OS X 10.11 style call: // // LLDB SENDS: jGetLoadedDynamicLibrariesInfos:{"image_count":1,"image_list_address":140734800075128} // STUB REPLIES: ${"images":[{"load_address":4294967296,"mod_date":0,"pathname":"/tmp/a.out","uuid":"02CF262C-ED6F-3965-9E14-63538B465CFF","mach_header":{"magic":4277009103,"cputype":16777223,"cpusubtype":18446744071562067971,"filetype":2},"segments":{"name":"__PAGEZERO","vmaddr":0,"vmsize":4294967296,"fileoff":0,"filesize":0,"maxprot":0},{"name":"__TEXT","vmaddr":4294967296,"vmsize":4096,"fileoff":0,"filesize":4096,"maxprot":7},{"name":"__LINKEDIT","vmaddr":4294971392,"vmsize":4096,"fileoff":4096,"filesize":152,"maxprot":7}}]}#00 // // Or pretty-printed, // // STUB REPLIES: ${"images": // [ // {"load_address":4294967296, // "mod_date":0, // "pathname":"/tmp/a.out", // "uuid":"02CF262C-ED6F-3965-9E14-63538B465CFF", // "mach_header": // {"magic":4277009103, // "cputype":16777223, // "cpusubtype":18446744071562067971, // "filetype":2 // }, // "segments": // [ // {"name":"__PAGEZERO", // "vmaddr":0, // "vmsize":4294967296, // "fileoff":0, // "filesize":0, // "maxprot":0 // }, // {"name":"__TEXT", // "vmaddr":4294967296, // "vmsize":4096, // "fileoff":0, // "filesize":4096, // "maxprot":7 // }, // {"name":"__LINKEDIT", // "vmaddr":4294971392, // "vmsize":4096, // "fileoff":4096, // "filesize":152, // "maxprot":7 // } // ] // } // ] // } // // // This is similar to the qXfer:libraries:read packet, and it could // be argued that it should be merged into that packet. A separate // packet was created primarily because lldb needs to specify the // number of images to be read and the address from which the initial // information is read. Also the XML DTD would need to be extended // quite a bit to provide all the information that the DynamicLoaderMacOSX // would need to work correctly on this platform. // // PRIORITY TO IMPLEMENT // On Mac OS X 10.11, iOS 9, tvOS 9, watchOS 2 and older: Low. If this packet is absent, // lldb will read the Mach-O headers/load commands out of memory. // On macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer: High. If this packet is absent, // lldb will not know anything about shared libraries in the inferior, or where the main // executable loaded. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // "jThreadsInfo" // // BRIEF // Ask for the server for thread stop information of all threads. // // PRIORITY TO IMPLEMENT // Low. This is a performance optimization, which speeds up debugging by avoiding // multiple round-trips for retrieving thread information. The information from this // packet can be retrieved using a combination of qThreadStopInfo and m packets. //---------------------------------------------------------------------- The data in this packet is very similar to the stop reply packets, but is packaged in JSON and uses JSON arrays where applicable. The JSON output looks like: [ { "tid":1580681, "metype":6, "medata":[2,0], "reason":"exception", "qaddr":140735118423168, "registers": { "0":"8000000000000000", "1":"0000000000000000", "2":"20fabf5fff7f0000", "3":"e8f8bf5fff7f0000", "4":"0100000000000000", "5":"d8f8bf5fff7f0000", "6":"b0f8bf5fff7f0000", "7":"20f4bf5fff7f0000", "8":"8000000000000000", "9":"61a8db78a61500db", "10":"3200000000000000", "11":"4602000000000000", "12":"0000000000000000", "13":"0000000000000000", "14":"0000000000000000", "15":"0000000000000000", "16":"960b000001000000", "17":"0202000000000000", "18":"2b00000000000000", "19":"0000000000000000", "20":"0000000000000000" }, "memory":[ {"address":140734799804592,"bytes":"c8f8bf5fff7f0000c9a59e8cff7f0000"}, {"address":140734799804616,"bytes":"00000000000000000100000000000000"} ] } ] It contains an array of dictionaries with all of the key value pairs that are normally in the stop reply packet, including the expedited registers. The registers are passed as hex-encoded JSON string in debuggee-endian byte order. Note that the register numbers are decimal numbers, unlike the stop-reply packet, where they are written in hex. The packet also contains expedited memory in the "memory" key. This allows the server to expedite memory that the client is likely to use (e.g., areas around the stack pointer, which are needed for computing backtraces) and it reduces the packet count. On MacOSX with debugserver, we expedite the frame pointer backchain for a thread (up to 256 entries) by reading 2 pointers worth of bytes at the frame pointer (for the previous FP and PC), and follow the backchain. Most backtraces on MacOSX and iOS now don't require us to read any memory! //---------------------------------------------------------------------- // "jGetSharedCacheInfo" // // BRIEF // This packet asks the remote debug stub to send the details about the inferior's // shared cache. The shared cache is a collection of common libraries/frameworks that // are mapped into every process at the same address on Darwin systems, and can be // identified by a load address and UUID. // // // LLDB SENDS: jGetSharedCacheInfo:{} // STUB REPLIES: ${"shared_cache_base_address":140735683125248,"shared_cache_uuid":"DDB8D70C-C9A2-3561-B2C8-BE48A4F33F96","no_shared_cache":false,"shared_cache_private_cache":false]}#00 // // PRIORITY TO IMPLEMENT // Low. When both lldb and the inferior process are running on the same computer, and lldb // and the inferior process have the same shared cache, lldb may (as an optimization) read // the shared cache out of its own memory instead of using gdb-remote read packets to read // them from the inferior process. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // "qQueryGDBServer" // // BRIEF // Ask the platform for the list of gdbservers we have to connect // // PRIORITY TO IMPLEMENT // Low. The packet is required to support connecting to gdbserver started // by the platform instance automatically. //---------------------------------------------------------------------- If the remote platform automatically started one or more gdbserver instance (without lldb asking it) then it have to return the list of port number or socket name for each of them what can be used by lldb to connect to those instances. The data in this packet is a JSON array of JSON objects with the following keys: "port": (optional) "socket_name": (optional) Example packet: [ { "port": 1234 }, { "port": 5432 }, { "socket_name": "foo" } ] Index: vendor/lldb/dist/include/lldb/API/SBStructuredData.h =================================================================== --- vendor/lldb/dist/include/lldb/API/SBStructuredData.h (revision 319149) +++ vendor/lldb/dist/include/lldb/API/SBStructuredData.h (revision 319150) @@ -1,47 +1,106 @@ //===-- SBStructuredData.h --------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef SBStructuredData_h #define SBStructuredData_h #include "lldb/API/SBDefines.h" #include "lldb/API/SBModule.h" namespace lldb { class SBStructuredData { public: SBStructuredData(); SBStructuredData(const lldb::SBStructuredData &rhs); SBStructuredData(const lldb::EventSP &event_sp); ~SBStructuredData(); lldb::SBStructuredData &operator=(const lldb::SBStructuredData &rhs); bool IsValid() const; lldb::SBError SetFromJSON(lldb::SBStream &stream); void Clear(); lldb::SBError GetAsJSON(lldb::SBStream &stream) const; lldb::SBError GetDescription(lldb::SBStream &stream) const; + //------------------------------------------------------------------ + /// Return the type of data in this data structure + //------------------------------------------------------------------ + lldb::StructuredDataType GetType() const; + + //------------------------------------------------------------------ + /// Return the size (i.e. number of elements) in this data structure + /// if it is an array or dictionary type. For other types, 0 will be + // returned. + //------------------------------------------------------------------ + size_t GetSize() const; + + //------------------------------------------------------------------ + /// Return the value corresponding to a key if this data structure + /// is a dictionary type. + //------------------------------------------------------------------ + lldb::SBStructuredData GetValueForKey(const char *key) const; + + //------------------------------------------------------------------ + /// Return the value corresponding to an index if this data structure + /// is array. + //------------------------------------------------------------------ + lldb::SBStructuredData GetItemAtIndex(size_t idx) const; + + //------------------------------------------------------------------ + /// Return the integer value if this data structure is an integer type. + //------------------------------------------------------------------ + uint64_t GetIntegerValue(uint64_t fail_value = 0) const; + + //------------------------------------------------------------------ + /// Return the floating point value if this data structure is a floating + /// type. + //------------------------------------------------------------------ + double GetFloatValue(double fail_value = 0.0) const; + + //------------------------------------------------------------------ + /// Return the boolean value if this data structure is a boolean type. + //------------------------------------------------------------------ + bool GetBooleanValue(bool fail_value = false) const; + + //------------------------------------------------------------------ + /// Provides the string value if this data structure is a string type. + /// + /// @param[out] dst + /// pointer where the string value will be written. In case it is null, + /// nothing will be written at @dst. + /// + /// @param[in] dst_len + /// max number of characters that can be written at @dst. In case it is + /// zero, nothing will be written at @dst. If this length is not enough + /// to write the complete string value, (dst_len-1) bytes of the string + /// value will be written at @dst followed by a null character. + /// + /// @return + /// Returns the byte size needed to completely write the string value at + /// @dst in all cases. + //------------------------------------------------------------------ + size_t GetStringValue(char *dst, size_t dst_len) const; + protected: friend class SBTraceOptions; StructuredDataImplUP m_impl_up; }; -} +} // namespace lldb #endif /* SBStructuredData_h */ Index: vendor/lldb/dist/include/lldb/API/SBTrace.h =================================================================== --- vendor/lldb/dist/include/lldb/API/SBTrace.h (revision 319149) +++ vendor/lldb/dist/include/lldb/API/SBTrace.h (revision 319150) @@ -1,122 +1,123 @@ //===-- SBTrace.h -----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLDB_SBTrace_h_ #define LLDB_SBTrace_h_ #include "lldb/API/SBDefines.h" #include "lldb/API/SBError.h" class TraceImpl; namespace lldb { class LLDB_API SBTrace { public: SBTrace(); //------------------------------------------------------------------ /// Obtain the trace data as raw bytes. /// /// @param[out] error /// An error explaining what went wrong. /// /// @param[in] buf /// Buffer to write the trace data to. /// /// @param[in] size /// The size of the buffer used to read the data. This is /// also the size of the data intended to read. It is also /// possible to partially read the trace data for some trace /// technologies by specifying a smaller buffer. /// /// @param[in] offset /// The start offset to begin reading the trace data. /// /// @param[in] thread_id /// Tracing could be started for the complete process or a - /// single thread, in the first case the uid obtained would + /// single thread, in the first case the traceid obtained would /// map to all the threads existing within the process and the /// ones spawning later. The thread_id parameter can be used in /// such a scenario to select the trace data for a specific /// thread. /// /// @return /// The size of the trace data effectively read by the API call. //------------------------------------------------------------------ size_t GetTraceData(SBError &error, void *buf, size_t size, size_t offset = 0, lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID); //------------------------------------------------------------------ /// Obtain any meta data as raw bytes for the tracing instance. /// The input parameter definition is similar to the previous /// function. //------------------------------------------------------------------ size_t GetMetaData(SBError &error, void *buf, size_t size, size_t offset = 0, lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID); //------------------------------------------------------------------ /// Stop the tracing instance. Stopping the trace will also /// lead to deletion of any gathered trace data. /// /// @param[out] error /// An error explaining what went wrong. /// /// @param[in] thread_id - /// The user id could map to a tracing instance for a thread + /// The trace id could map to a tracing instance for a thread /// or could also map to a group of threads being traced with /// the same trace options. A thread_id is normally optional /// except in the case of tracing a complete process and tracing /// needs to switched off on a particular thread. /// A situation could occur where initially a thread (lets say - /// thread A) is being individually traced with a particular uid - /// and then tracing is started on the complete process, in this - /// case thread A will continue without any change. All newly - /// spawned threads would be traced with the uid of the process. + /// thread A) is being individually traced with a particular + /// trace id and then tracing is started on the complete + /// process, in this case thread A will continue without any + /// change. All newly spawned threads would be traced with the + /// trace id of the process. /// Now if the StopTrace API is called for the whole process, /// thread A will not be stopped and must be stopped separately. //------------------------------------------------------------------ void StopTrace(SBError &error, lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID); //------------------------------------------------------------------ /// Get the trace configuration being used for the trace instance. /// The threadid in the SBTraceOptions needs to be set when the /// configuration used by a specific thread is being requested. /// /// @param[out] options /// The trace options actually used by the trace instance /// would be filled by the API. /// /// @param[out] error /// An error explaining what went wrong. //------------------------------------------------------------------ void GetTraceConfig(SBTraceOptions &options, SBError &error); lldb::user_id_t GetTraceUID(); bool IsValid(); protected: typedef std::shared_ptr TraceImplSP; friend class SBProcess; void SetTraceUID(lldb::user_id_t uid); TraceImplSP m_trace_impl_sp; lldb::ProcessSP GetSP() const; void SetSP(const ProcessSP &process_sp); lldb::ProcessWP m_opaque_wp; }; } // namespace lldb #endif // LLDB_SBTrace_h_ Index: vendor/lldb/dist/include/lldb/Core/StructuredData.h =================================================================== --- vendor/lldb/dist/include/lldb/Core/StructuredData.h (revision 319149) +++ vendor/lldb/dist/include/lldb/Core/StructuredData.h (revision 319150) @@ -1,558 +1,560 @@ //===-- StructuredData.h ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_StructuredData_h_ #define liblldb_StructuredData_h_ #include "llvm/ADT/StringRef.h" #include "lldb/Utility/ConstString.h" -#include "lldb/Utility/FileSpec.h" // for FileSpec +#include "lldb/Utility/FileSpec.h" // for FileSpec +#include "lldb/lldb-enumerations.h" // for StructuredDataType #include #include #include #include #include // for move #include #include #include // for assert #include // for size_t #include // for uint64_t namespace lldb_private { class Status; } namespace lldb_private { class Stream; } namespace lldb_private { //---------------------------------------------------------------------- /// @class StructuredData StructuredData.h "lldb/Core/StructuredData.h" /// @brief A class which can hold structured data /// /// The StructuredData class is designed to hold the data from a JSON /// or plist style file -- a serialized data structure with dictionaries /// (maps, hashes), arrays, and concrete values like integers, floating /// point numbers, strings, booleans. /// /// StructuredData does not presuppose any knowledge of the schema for /// the data it is holding; it can parse JSON data, for instance, and /// other parts of lldb can iterate through the parsed data set to find /// keys and values that may be present. //---------------------------------------------------------------------- class StructuredData { public: class Object; class Array; class Integer; class Float; class Boolean; class String; class Dictionary; class Generic; typedef std::shared_ptr ObjectSP; typedef std::shared_ptr ArraySP; typedef std::shared_ptr IntegerSP; typedef std::shared_ptr FloatSP; typedef std::shared_ptr BooleanSP; typedef std::shared_ptr StringSP; typedef std::shared_ptr DictionarySP; typedef std::shared_ptr GenericSP; - enum class Type { - eTypeInvalid = -1, - eTypeNull = 0, - eTypeGeneric, - eTypeArray, - eTypeInteger, - eTypeFloat, - eTypeBoolean, - eTypeString, - eTypeDictionary - }; - class Object : public std::enable_shared_from_this { public: - Object(Type t = Type::eTypeInvalid) : m_type(t) {} + Object(lldb::StructuredDataType t = lldb::eStructuredDataTypeInvalid) + : m_type(t) {} virtual ~Object() = default; virtual bool IsValid() const { return true; } - virtual void Clear() { m_type = Type::eTypeInvalid; } + virtual void Clear() { m_type = lldb::eStructuredDataTypeInvalid; } - Type GetType() const { return m_type; } + lldb::StructuredDataType GetType() const { return m_type; } - void SetType(Type t) { m_type = t; } + void SetType(lldb::StructuredDataType t) { m_type = t; } Array *GetAsArray() { - return ((m_type == Type::eTypeArray) ? static_cast(this) - : nullptr); + return ((m_type == lldb::eStructuredDataTypeArray) + ? static_cast(this) + : nullptr); } Dictionary *GetAsDictionary() { - return ((m_type == Type::eTypeDictionary) - ? static_cast(this) - : nullptr); + return ( + (m_type == lldb::eStructuredDataTypeDictionary) + ? static_cast(this) + : nullptr); } Integer *GetAsInteger() { - return ((m_type == Type::eTypeInteger) ? static_cast(this) - : nullptr); + return ((m_type == lldb::eStructuredDataTypeInteger) + ? static_cast(this) + : nullptr); } uint64_t GetIntegerValue(uint64_t fail_value = 0) { Integer *integer = GetAsInteger(); return ((integer != nullptr) ? integer->GetValue() : fail_value); } Float *GetAsFloat() { - return ((m_type == Type::eTypeFloat) ? static_cast(this) - : nullptr); + return ((m_type == lldb::eStructuredDataTypeFloat) + ? static_cast(this) + : nullptr); } double GetFloatValue(double fail_value = 0.0) { Float *f = GetAsFloat(); return ((f != nullptr) ? f->GetValue() : fail_value); } Boolean *GetAsBoolean() { - return ((m_type == Type::eTypeBoolean) ? static_cast(this) - : nullptr); + return ((m_type == lldb::eStructuredDataTypeBoolean) + ? static_cast(this) + : nullptr); } bool GetBooleanValue(bool fail_value = false) { Boolean *b = GetAsBoolean(); return ((b != nullptr) ? b->GetValue() : fail_value); } String *GetAsString() { - return ((m_type == Type::eTypeString) ? static_cast(this) - : nullptr); + return ((m_type == lldb::eStructuredDataTypeString) + ? static_cast(this) + : nullptr); } llvm::StringRef GetStringValue(const char *fail_value = nullptr) { String *s = GetAsString(); if (s) return s->GetValue(); return fail_value; } Generic *GetAsGeneric() { - return ((m_type == Type::eTypeGeneric) ? static_cast(this) - : nullptr); + return ((m_type == lldb::eStructuredDataTypeGeneric) + ? static_cast(this) + : nullptr); } ObjectSP GetObjectForDotSeparatedPath(llvm::StringRef path); void DumpToStdout(bool pretty_print = true) const; virtual void Dump(Stream &s, bool pretty_print = true) const = 0; private: - Type m_type; + lldb::StructuredDataType m_type; }; class Array : public Object { public: - Array() : Object(Type::eTypeArray) {} + Array() : Object(lldb::eStructuredDataTypeArray) {} ~Array() override = default; bool ForEach(std::function const &foreach_callback) const { for (const auto &object_sp : m_items) { if (foreach_callback(object_sp.get()) == false) return false; } return true; } size_t GetSize() const { return m_items.size(); } ObjectSP operator[](size_t idx) { if (idx < m_items.size()) return m_items[idx]; return ObjectSP(); } ObjectSP GetItemAtIndex(size_t idx) const { assert(idx < GetSize()); if (idx < m_items.size()) return m_items[idx]; return ObjectSP(); } template bool GetItemAtIndexAsInteger(size_t idx, IntType &result) const { ObjectSP value_sp = GetItemAtIndex(idx); if (value_sp.get()) { if (auto int_value = value_sp->GetAsInteger()) { result = static_cast(int_value->GetValue()); return true; } } return false; } template bool GetItemAtIndexAsInteger(size_t idx, IntType &result, IntType default_val) const { bool success = GetItemAtIndexAsInteger(idx, result); if (!success) result = default_val; return success; } bool GetItemAtIndexAsString(size_t idx, llvm::StringRef &result) const { ObjectSP value_sp = GetItemAtIndex(idx); if (value_sp.get()) { if (auto string_value = value_sp->GetAsString()) { result = string_value->GetValue(); return true; } } return false; } bool GetItemAtIndexAsString(size_t idx, llvm::StringRef &result, llvm::StringRef default_val) const { bool success = GetItemAtIndexAsString(idx, result); if (!success) result = default_val; return success; } bool GetItemAtIndexAsString(size_t idx, ConstString &result) const { ObjectSP value_sp = GetItemAtIndex(idx); if (value_sp.get()) { if (auto string_value = value_sp->GetAsString()) { result = ConstString(string_value->GetValue()); return true; } } return false; } bool GetItemAtIndexAsString(size_t idx, ConstString &result, const char *default_val) const { bool success = GetItemAtIndexAsString(idx, result); if (!success) result.SetCString(default_val); return success; } bool GetItemAtIndexAsDictionary(size_t idx, Dictionary *&result) const { result = nullptr; ObjectSP value_sp = GetItemAtIndex(idx); if (value_sp.get()) { result = value_sp->GetAsDictionary(); return (result != nullptr); } return false; } bool GetItemAtIndexAsArray(size_t idx, Array *&result) const { result = nullptr; ObjectSP value_sp = GetItemAtIndex(idx); if (value_sp.get()) { result = value_sp->GetAsArray(); return (result != nullptr); } return false; } void Push(ObjectSP item) { m_items.push_back(item); } void AddItem(ObjectSP item) { m_items.push_back(item); } void Dump(Stream &s, bool pretty_print = true) const override; protected: typedef std::vector collection; collection m_items; }; class Integer : public Object { public: - Integer(uint64_t i = 0) : Object(Type::eTypeInteger), m_value(i) {} + Integer(uint64_t i = 0) + : Object(lldb::eStructuredDataTypeInteger), m_value(i) {} ~Integer() override = default; void SetValue(uint64_t value) { m_value = value; } uint64_t GetValue() { return m_value; } void Dump(Stream &s, bool pretty_print = true) const override; protected: uint64_t m_value; }; class Float : public Object { public: - Float(double d = 0.0) : Object(Type::eTypeFloat), m_value(d) {} + Float(double d = 0.0) : Object(lldb::eStructuredDataTypeFloat), + m_value(d) {} ~Float() override = default; void SetValue(double value) { m_value = value; } double GetValue() { return m_value; } void Dump(Stream &s, bool pretty_print = true) const override; protected: double m_value; }; class Boolean : public Object { public: - Boolean(bool b = false) : Object(Type::eTypeBoolean), m_value(b) {} + Boolean(bool b = false) : Object(lldb::eStructuredDataTypeBoolean), + m_value(b) {} ~Boolean() override = default; void SetValue(bool value) { m_value = value; } bool GetValue() { return m_value; } void Dump(Stream &s, bool pretty_print = true) const override; protected: bool m_value; }; class String : public Object { public: - String() : Object(Type::eTypeString) {} + String() : Object(lldb::eStructuredDataTypeString) {} explicit String(llvm::StringRef S) - : Object(Type::eTypeString), m_value(S) {} + : Object(lldb::eStructuredDataTypeString), + m_value(S) {} void SetValue(llvm::StringRef S) { m_value = S; } llvm::StringRef GetValue() { return m_value; } void Dump(Stream &s, bool pretty_print = true) const override; protected: std::string m_value; }; class Dictionary : public Object { public: - Dictionary() : Object(Type::eTypeDictionary), m_dict() {} + Dictionary() : Object(lldb::eStructuredDataTypeDictionary), + m_dict() {} ~Dictionary() override = default; size_t GetSize() const { return m_dict.size(); } void ForEach(std::function const &callback) const { for (const auto &pair : m_dict) { if (callback(pair.first, pair.second.get()) == false) break; } } ObjectSP GetKeys() const { auto object_sp = std::make_shared(); collection::const_iterator iter; for (iter = m_dict.begin(); iter != m_dict.end(); ++iter) { auto key_object_sp = std::make_shared(); key_object_sp->SetValue(iter->first.AsCString()); object_sp->Push(key_object_sp); } return object_sp; } ObjectSP GetValueForKey(llvm::StringRef key) const { ObjectSP value_sp; if (!key.empty()) { ConstString key_cs(key); collection::const_iterator iter = m_dict.find(key_cs); if (iter != m_dict.end()) value_sp = iter->second; } return value_sp; } bool GetValueForKeyAsBoolean(llvm::StringRef key, bool &result) const { bool success = false; ObjectSP value_sp = GetValueForKey(key); if (value_sp.get()) { Boolean *result_ptr = value_sp->GetAsBoolean(); if (result_ptr) { result = result_ptr->GetValue(); success = true; } } return success; } template bool GetValueForKeyAsInteger(llvm::StringRef key, IntType &result) const { ObjectSP value_sp = GetValueForKey(key); if (value_sp) { if (auto int_value = value_sp->GetAsInteger()) { result = static_cast(int_value->GetValue()); return true; } } return false; } template bool GetValueForKeyAsInteger(llvm::StringRef key, IntType &result, IntType default_val) const { bool success = GetValueForKeyAsInteger(key, result); if (!success) result = default_val; return success; } bool GetValueForKeyAsString(llvm::StringRef key, llvm::StringRef &result) const { ObjectSP value_sp = GetValueForKey(key); if (value_sp.get()) { if (auto string_value = value_sp->GetAsString()) { result = string_value->GetValue(); return true; } } return false; } bool GetValueForKeyAsString(llvm::StringRef key, llvm::StringRef &result, const char *default_val) const { bool success = GetValueForKeyAsString(key, result); if (!success) { if (default_val) result = default_val; else result = llvm::StringRef(); } return success; } bool GetValueForKeyAsString(llvm::StringRef key, ConstString &result) const { ObjectSP value_sp = GetValueForKey(key); if (value_sp.get()) { if (auto string_value = value_sp->GetAsString()) { result = ConstString(string_value->GetValue()); return true; } } return false; } bool GetValueForKeyAsString(llvm::StringRef key, ConstString &result, const char *default_val) const { bool success = GetValueForKeyAsString(key, result); if (!success) result.SetCString(default_val); return success; } bool GetValueForKeyAsDictionary(llvm::StringRef key, Dictionary *&result) const { result = nullptr; ObjectSP value_sp = GetValueForKey(key); if (value_sp.get()) { result = value_sp->GetAsDictionary(); return (result != nullptr); } return false; } bool GetValueForKeyAsArray(llvm::StringRef key, Array *&result) const { result = nullptr; ObjectSP value_sp = GetValueForKey(key); if (value_sp.get()) { result = value_sp->GetAsArray(); return (result != nullptr); } return false; } bool HasKey(llvm::StringRef key) const { ConstString key_cs(key); collection::const_iterator search = m_dict.find(key_cs); return search != m_dict.end(); } void AddItem(llvm::StringRef key, ObjectSP value_sp) { ConstString key_cs(key); m_dict[key_cs] = value_sp; } void AddIntegerItem(llvm::StringRef key, uint64_t value) { AddItem(key, std::make_shared(value)); } void AddFloatItem(llvm::StringRef key, double value) { AddItem(key, std::make_shared(value)); } void AddStringItem(llvm::StringRef key, llvm::StringRef value) { AddItem(key, std::make_shared(std::move(value))); } void AddBooleanItem(llvm::StringRef key, bool value) { AddItem(key, std::make_shared(value)); } void Dump(Stream &s, bool pretty_print = true) const override; protected: typedef std::map collection; collection m_dict; }; class Null : public Object { public: - Null() : Object(Type::eTypeNull) {} + Null() : Object(lldb::eStructuredDataTypeNull) {} ~Null() override = default; bool IsValid() const override { return false; } void Dump(Stream &s, bool pretty_print = true) const override; }; class Generic : public Object { public: explicit Generic(void *object = nullptr) - : Object(Type::eTypeGeneric), m_object(object) {} + : Object(lldb::eStructuredDataTypeGeneric), m_object(object) {} void SetValue(void *value) { m_object = value; } void *GetValue() const { return m_object; } bool IsValid() const override { return m_object != nullptr; } void Dump(Stream &s, bool pretty_print = true) const override; private: void *m_object; }; static ObjectSP ParseJSON(std::string json_text); static ObjectSP ParseJSONFromFile(const FileSpec &file, Status &error); }; } // namespace lldb_private #endif // liblldb_StructuredData_h_ Index: vendor/lldb/dist/include/lldb/Core/StructuredDataImpl.h =================================================================== --- vendor/lldb/dist/include/lldb/Core/StructuredDataImpl.h (revision 319149) +++ vendor/lldb/dist/include/lldb/Core/StructuredDataImpl.h (revision 319150) @@ -1,95 +1,156 @@ //===-- StructuredDataImpl.h ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_StructuredDataImpl_h_ #define liblldb_StructuredDataImpl_h_ #include "lldb/Core/Event.h" #include "lldb/Core/StructuredData.h" #include "lldb/Target/StructuredDataPlugin.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/Stream.h" +#include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" +#include "llvm/ADT/StringRef.h" #pragma mark-- #pragma mark StructuredDataImpl namespace lldb_private { class StructuredDataImpl { public: StructuredDataImpl() : m_plugin_wp(), m_data_sp() {} StructuredDataImpl(const StructuredDataImpl &rhs) = default; StructuredDataImpl(const lldb::EventSP &event_sp) : m_plugin_wp( EventDataStructuredData::GetPluginFromEvent(event_sp.get())), m_data_sp(EventDataStructuredData::GetObjectFromEvent(event_sp.get())) { } ~StructuredDataImpl() = default; StructuredDataImpl &operator=(const StructuredDataImpl &rhs) = default; bool IsValid() const { return m_data_sp.get() != nullptr; } void Clear() { m_plugin_wp.reset(); m_data_sp.reset(); } Status GetAsJSON(Stream &stream) const { Status error; if (!m_data_sp) { error.SetErrorString("No structured data."); return error; } m_data_sp->Dump(stream); return error; } Status GetDescription(Stream &stream) const { Status error; if (!m_data_sp) { error.SetErrorString("Cannot pretty print structured data: " "no data to print."); return error; } // Grab the plugin. auto plugin_sp = lldb::StructuredDataPluginSP(m_plugin_wp); if (!plugin_sp) { error.SetErrorString("Cannot pretty print structured data: " "plugin doesn't exist."); return error; } // Get the data's description. return plugin_sp->GetDescription(m_data_sp, stream); } - StructuredData::ObjectSP GetObjectSP() { - return m_data_sp; + StructuredData::ObjectSP GetObjectSP() { return m_data_sp; } + + void SetObjectSP(const StructuredData::ObjectSP &obj) { m_data_sp = obj; } + + lldb::StructuredDataType GetType() const { + return (m_data_sp ? m_data_sp->GetType() : + lldb::eStructuredDataTypeInvalid); } - void SetObjectSP(const StructuredData::ObjectSP &obj) { - m_data_sp = obj; + size_t GetSize() const { + if (!m_data_sp) + return 0; + + if (m_data_sp->GetType() == lldb::eStructuredDataTypeDictionary) { + auto dict = m_data_sp->GetAsDictionary(); + return (dict->GetSize()); + } else if (m_data_sp->GetType() == lldb::eStructuredDataTypeArray) { + auto array = m_data_sp->GetAsArray(); + return (array->GetSize()); + } else + return 0; } -private: + StructuredData::ObjectSP GetValueForKey(const char *key) const { + if (m_data_sp) { + auto dict = m_data_sp->GetAsDictionary(); + if (dict) + return dict->GetValueForKey(llvm::StringRef(key)); + } + return StructuredData::ObjectSP(); + } + StructuredData::ObjectSP GetItemAtIndex(size_t idx) const { + if (m_data_sp) { + auto array = m_data_sp->GetAsArray(); + if (array) + return array->GetItemAtIndex(idx); + } + return StructuredData::ObjectSP(); + } + + uint64_t GetIntegerValue(uint64_t fail_value = 0) const { + return (m_data_sp ? m_data_sp->GetIntegerValue(fail_value) : fail_value); + } + + double GetFloatValue(double fail_value = 0.0) const { + return (m_data_sp ? m_data_sp->GetFloatValue(fail_value) : fail_value); + } + + bool GetBooleanValue(bool fail_value = false) const { + return (m_data_sp ? m_data_sp->GetBooleanValue(fail_value) : fail_value); + } + + size_t GetStringValue(char *dst, size_t dst_len) const { + if (!m_data_sp) + return 0; + + llvm::StringRef result = m_data_sp->GetStringValue(); + if (result.empty()) + return 0; + + if (!dst || !dst_len) { + char s[1]; + return (::snprintf(s, 1, "%s", result.data())); + } + return (::snprintf(dst, dst_len, "%s", result.data())); + } + +private: lldb::StructuredDataPluginWP m_plugin_wp; StructuredData::ObjectSP m_data_sp; }; -} +} // namespace lldb_private #endif Index: vendor/lldb/dist/include/lldb/Core/TraceOptions.h =================================================================== --- vendor/lldb/dist/include/lldb/Core/TraceOptions.h (revision 319149) +++ vendor/lldb/dist/include/lldb/Core/TraceOptions.h (revision 319150) @@ -1,62 +1,61 @@ //===-- TraceOptions.h ------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_TraceOptions_h_ #define liblldb_TraceOptions_h_ #include "lldb/lldb-defines.h" #include "lldb/lldb-enumerations.h" #include "lldb/Core/StructuredData.h" namespace lldb_private { class TraceOptions { public: - TraceOptions() - : m_trace_params(new StructuredData::Dictionary()) {} + TraceOptions() : m_trace_params(new StructuredData::Dictionary()) {} const StructuredData::DictionarySP &getTraceParams() const { return m_trace_params; } lldb::TraceType getType() const { return m_type; } uint64_t getTraceBufferSize() const { return m_trace_buffer_size; } uint64_t getMetaDataBufferSize() const { return m_meta_data_buffer_size; } void setTraceParams(const StructuredData::DictionarySP &dict_obj) { m_trace_params = dict_obj; } void setType(lldb::TraceType type) { m_type = type; } void setTraceBufferSize(uint64_t size) { m_trace_buffer_size = size; } void setMetaDataBufferSize(uint64_t size) { m_meta_data_buffer_size = size; } void setThreadID(lldb::tid_t thread_id) { m_thread_id = thread_id; } - lldb::tid_t getThreadID() { return m_thread_id; } + lldb::tid_t getThreadID() const { return m_thread_id; } private: lldb::TraceType m_type; uint64_t m_trace_buffer_size; uint64_t m_meta_data_buffer_size; lldb::tid_t m_thread_id; /// m_trace_params is meant to hold any custom parameters /// apart from meta buffer size and trace size. /// The interpretation of such parameters is left to /// the lldb-server. StructuredData::DictionarySP m_trace_params; }; } #endif // liblldb_TraceOptions_h_ Index: vendor/lldb/dist/include/lldb/Host/Editline.h =================================================================== --- vendor/lldb/dist/include/lldb/Host/Editline.h (revision 319149) +++ vendor/lldb/dist/include/lldb/Host/Editline.h (revision 319150) @@ -1,368 +1,374 @@ //===-- Editline.h ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // TODO: wire up window size changes // If we ever get a private copy of libedit, there are a number of defects that // would be nice to fix; // a) Sometimes text just disappears while editing. In an 80-column editor // paste the following text, without // the quotes: // "This is a test of the input system missing Hello, World! Do you // disappear when it gets to a particular length?" // Now press ^A to move to the start and type 3 characters, and you'll see a // good amount of the text will // disappear. It's still in the buffer, just invisible. // b) The prompt printing logic for dealing with ANSI formatting characters is // broken, which is why we're // working around it here. // c) When resizing the terminal window, if the cursor moves between rows // libedit will get confused. // d) The incremental search uses escape to cancel input, so it's confused by // ANSI sequences starting with escape. // e) Emoji support is fairly terrible, presumably it doesn't understand // composed characters? #ifndef liblldb_Editline_h_ #define liblldb_Editline_h_ #if defined(__cplusplus) #include #include #include // components needed to handle wide characters ( , codecvt_utf8, // libedit built with '--enable-widec' ) // are available on some platforms. The wchar_t versions of libedit functions // will only be // used in cases where this is true. This is a compile time dependecy, for now // selected per target Platform #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ defined(__OpenBSD__) #define LLDB_EDITLINE_USE_WCHAR 1 #include #else #define LLDB_EDITLINE_USE_WCHAR 0 #endif #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/lldb-private.h" #if defined(_WIN32) #include "lldb/Host/windows/editlinewin.h" #elif !defined(__ANDROID__) #include #endif #include #include #include #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Predicate.h" #include "lldb/Utility/FileSpec.h" namespace lldb_private { namespace line_editor { // type alias's to help manage 8 bit and wide character versions of libedit #if LLDB_EDITLINE_USE_WCHAR using EditLineStringType = std::wstring; using EditLineStringStreamType = std::wstringstream; using EditLineCharType = wchar_t; #else using EditLineStringType = std::string; using EditLineStringStreamType = std::stringstream; using EditLineCharType = char; #endif +#ifdef EL_CLIENTDATA /* editline with wide support + wide char read function */ +using EditLineGetCharType = wchar_t; +#else +using EditLineGetCharType = char; +#endif + typedef int (*EditlineGetCharCallbackType)(::EditLine *editline, - EditLineCharType *c); + EditLineGetCharType *c); typedef unsigned char (*EditlineCommandCallbackType)(::EditLine *editline, int ch); typedef const char *(*EditlinePromptCallbackType)(::EditLine *editline); class EditlineHistory; typedef std::shared_ptr EditlineHistorySP; typedef bool (*IsInputCompleteCallbackType)(Editline *editline, StringList &lines, void *baton); typedef int (*FixIndentationCallbackType)(Editline *editline, const StringList &lines, int cursor_position, void *baton); typedef int (*CompleteCallbackType)(const char *current_line, const char *cursor, const char *last_char, int skip_first_n_matches, int max_matches, StringList &matches, void *baton); /// Status used to decide when and how to start editing another line in /// multi-line sessions enum class EditorStatus { /// The default state proceeds to edit the current line Editing, /// Editing complete, returns the complete set of edited lines Complete, /// End of input reported EndOfInput, /// Editing interrupted Interrupted }; /// Established locations that can be easily moved among with MoveCursor enum class CursorLocation { /// The start of the first line in a multi-line edit session BlockStart, /// The start of the current line in a multi-line edit session EditingPrompt, /// The location of the cursor on the current line in a multi-line edit /// session EditingCursor, /// The location immediately after the last character in a multi-line edit /// session BlockEnd }; } using namespace line_editor; /// Instances of Editline provide an abstraction over libedit's EditLine /// facility. Both /// single- and multi-line editing are supported. class Editline { public: Editline(const char *editor_name, FILE *input_file, FILE *output_file, FILE *error_file, bool color_prompts); ~Editline(); /// Uses the user data storage of EditLine to retrieve an associated instance /// of Editline. static Editline *InstanceFor(::EditLine *editline); /// Sets a string to be used as a prompt, or combined with a line number to /// form a prompt. void SetPrompt(const char *prompt); /// Sets an alternate string to be used as a prompt for the second line and /// beyond in multi-line /// editing scenarios. void SetContinuationPrompt(const char *continuation_prompt); /// Required to update the width of the terminal registered for I/O. It is /// critical that this /// be correct at all times. void TerminalSizeChanged(); /// Returns the prompt established by SetPrompt() const char *GetPrompt(); /// Returns the index of the line currently being edited uint32_t GetCurrentLine(); /// Interrupt the current edit as if ^C was pressed bool Interrupt(); /// Cancel this edit and oblitarate all trace of it bool Cancel(); /// Register a callback for the tab key void SetAutoCompleteCallback(CompleteCallbackType callback, void *baton); /// Register a callback for testing whether multi-line input is complete void SetIsInputCompleteCallback(IsInputCompleteCallbackType callback, void *baton); /// Register a callback for determining the appropriate indentation for a line /// when creating a newline. An optional set of insertable characters can /// also /// trigger the callback. bool SetFixIndentationCallback(FixIndentationCallbackType callback, void *baton, const char *indent_chars); /// Prompts for and reads a single line of user input. bool GetLine(std::string &line, bool &interrupted); /// Prompts for and reads a multi-line batch of user input. bool GetLines(int first_line_number, StringList &lines, bool &interrupted); void PrintAsync(Stream *stream, const char *s, size_t len); private: /// Sets the lowest line number for multi-line editing sessions. A value of /// zero suppresses /// line number printing in the prompt. void SetBaseLineNumber(int line_number); /// Returns the complete prompt by combining the prompt or continuation prompt /// with line numbers /// as appropriate. The line index is a zero-based index into the current /// multi-line session. std::string PromptForIndex(int line_index); /// Sets the current line index between line edits to allow free movement /// between lines. Updates /// the prompt to match. void SetCurrentLine(int line_index); /// Determines the width of the prompt in characters. The width is guaranteed /// to be the same for /// all lines of the current multi-line session. int GetPromptWidth(); /// Returns true if the underlying EditLine session's keybindings are /// Emacs-based, or false if /// they are VI-based. bool IsEmacs(); /// Returns true if the current EditLine buffer contains nothing but spaces, /// or is empty. bool IsOnlySpaces(); /// Helper method used by MoveCursor to determine relative line position. int GetLineIndexForLocation(CursorLocation location, int cursor_row); /// Move the cursor from one well-established location to another using /// relative line positioning /// and absolute column positioning. void MoveCursor(CursorLocation from, CursorLocation to); /// Clear from cursor position to bottom of screen and print input lines /// including prompts, optionally /// starting from a specific line. Lines are drawn with an extra space at the /// end to reserve room for /// the rightmost cursor position. void DisplayInput(int firstIndex = 0); /// Counts the number of rows a given line of content will end up occupying, /// taking into account both /// the preceding prompt and a single trailing space occupied by a cursor when /// at the end of the line. int CountRowsForLine(const EditLineStringType &content); /// Save the line currently being edited void SaveEditedLine(); /// Convert the current input lines into a UTF8 StringList StringList GetInputAsStringList(int line_count = UINT32_MAX); /// Replaces the current multi-line session with the next entry from history. /// When the parameter is /// true it will take the next earlier entry from history, when it is false it /// takes the next most /// recent. unsigned char RecallHistory(bool earlier); /// Character reading implementation for EditLine that supports our multi-line /// editing trickery. - int GetCharacter(EditLineCharType *c); + int GetCharacter(EditLineGetCharType *c); /// Prompt implementation for EditLine. const char *Prompt(); /// Line break command used when meta+return is pressed in multi-line mode. unsigned char BreakLineCommand(int ch); /// Command used when return is pressed in multi-line mode. unsigned char EndOrAddLineCommand(int ch); /// Delete command used when delete is pressed in multi-line mode. unsigned char DeleteNextCharCommand(int ch); /// Delete command used when backspace is pressed in multi-line mode. unsigned char DeletePreviousCharCommand(int ch); /// Line navigation command used when ^P or up arrow are pressed in multi-line /// mode. unsigned char PreviousLineCommand(int ch); /// Line navigation command used when ^N or down arrow are pressed in /// multi-line mode. unsigned char NextLineCommand(int ch); /// History navigation command used when Alt + up arrow is pressed in /// multi-line mode. unsigned char PreviousHistoryCommand(int ch); /// History navigation command used when Alt + down arrow is pressed in /// multi-line mode. unsigned char NextHistoryCommand(int ch); /// Buffer start command used when Esc < is typed in multi-line emacs mode. unsigned char BufferStartCommand(int ch); /// Buffer end command used when Esc > is typed in multi-line emacs mode. unsigned char BufferEndCommand(int ch); /// Context-sensitive tab insertion or code completion command used when the /// tab key is typed. unsigned char TabCommand(int ch); /// Respond to normal character insertion by fixing line indentation unsigned char FixIndentationCommand(int ch); /// Revert line command used when moving between lines. unsigned char RevertLineCommand(int ch); /// Ensures that the current EditLine instance is properly configured for /// single or multi-line editing. void ConfigureEditor(bool multiline); - bool CompleteCharacter(char ch, EditLineCharType &out); + bool CompleteCharacter(char ch, EditLineGetCharType &out); private: #if LLDB_EDITLINE_USE_WCHAR std::wstring_convert> m_utf8conv; #endif ::EditLine *m_editline = nullptr; EditlineHistorySP m_history_sp; bool m_in_history = false; std::vector m_live_history_lines; bool m_multiline_enabled = false; std::vector m_input_lines; EditorStatus m_editor_status; bool m_color_prompts = true; int m_terminal_width = 0; int m_base_line_number = 0; unsigned m_current_line_index = 0; int m_current_line_rows = -1; int m_revert_cursor_index = 0; int m_line_number_digits = 3; std::string m_set_prompt; std::string m_set_continuation_prompt; std::string m_current_prompt; bool m_needs_prompt_repaint = false; std::string m_editor_name; FILE *m_input_file; FILE *m_output_file; FILE *m_error_file; ConnectionFileDescriptor m_input_connection; IsInputCompleteCallbackType m_is_input_complete_callback = nullptr; void *m_is_input_complete_callback_baton = nullptr; FixIndentationCallbackType m_fix_indentation_callback = nullptr; void *m_fix_indentation_callback_baton = nullptr; const char *m_fix_indentation_callback_chars = nullptr; CompleteCallbackType m_completion_callback = nullptr; void *m_completion_callback_baton = nullptr; std::mutex m_output_mutex; }; } #endif // #if defined(__cplusplus) #endif // liblldb_Editline_h_ Index: vendor/lldb/dist/include/lldb/Host/common/NativeProcessProtocol.h =================================================================== --- vendor/lldb/dist/include/lldb/Host/common/NativeProcessProtocol.h (revision 319149) +++ vendor/lldb/dist/include/lldb/Host/common/NativeProcessProtocol.h (revision 319150) @@ -1,386 +1,489 @@ //===-- NativeProcessProtocol.h ---------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_NativeProcessProtocol_h_ #define liblldb_NativeProcessProtocol_h_ +#include "lldb/Core/TraceOptions.h" #include "lldb/Host/MainLoop.h" #include "lldb/Utility/Status.h" #include "lldb/lldb-private-forward.h" #include "lldb/lldb-types.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/MemoryBuffer.h" #include #include "NativeBreakpointList.h" #include "NativeWatchpointList.h" namespace lldb_private { class MemoryRegionInfo; class ResumeActionList; //------------------------------------------------------------------ // NativeProcessProtocol //------------------------------------------------------------------ class NativeProcessProtocol : public std::enable_shared_from_this { friend class SoftwareBreakpoint; public: virtual ~NativeProcessProtocol() {} virtual Status Resume(const ResumeActionList &resume_actions) = 0; virtual Status Halt() = 0; virtual Status Detach() = 0; //------------------------------------------------------------------ /// Sends a process a UNIX signal \a signal. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Status Signal(int signo) = 0; //------------------------------------------------------------------ /// Tells a process to interrupt all operations as if by a Ctrl-C. /// /// The default implementation will send a local host's equivalent of /// a SIGSTOP to the process via the NativeProcessProtocol::Signal() /// operation. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Status Interrupt(); virtual Status Kill() = 0; //------------------------------------------------------------------ // Tells a process not to stop the inferior on given signals // and just reinject them back. //------------------------------------------------------------------ virtual Status IgnoreSignals(llvm::ArrayRef signals); //---------------------------------------------------------------------- // Memory and memory region functions //---------------------------------------------------------------------- virtual Status GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info); virtual Status ReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) = 0; virtual Status ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) = 0; virtual Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) = 0; virtual Status AllocateMemory(size_t size, uint32_t permissions, lldb::addr_t &addr) = 0; virtual Status DeallocateMemory(lldb::addr_t addr) = 0; virtual lldb::addr_t GetSharedLibraryInfoAddress() = 0; virtual bool IsAlive() const; virtual size_t UpdateThreads() = 0; virtual bool GetArchitecture(ArchSpec &arch) const = 0; //---------------------------------------------------------------------- // Breakpoint functions //---------------------------------------------------------------------- virtual Status SetBreakpoint(lldb::addr_t addr, uint32_t size, bool hardware) = 0; virtual Status RemoveBreakpoint(lldb::addr_t addr, bool hardware = false); virtual Status EnableBreakpoint(lldb::addr_t addr); virtual Status DisableBreakpoint(lldb::addr_t addr); //---------------------------------------------------------------------- // Hardware Breakpoint functions //---------------------------------------------------------------------- virtual const HardwareBreakpointMap &GetHardwareBreakpointMap() const; virtual Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size); virtual Status RemoveHardwareBreakpoint(lldb::addr_t addr); //---------------------------------------------------------------------- // Watchpoint functions //---------------------------------------------------------------------- virtual const NativeWatchpointList::WatchpointMap &GetWatchpointMap() const; virtual llvm::Optional> GetHardwareDebugSupportInfo() const; virtual Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware); virtual Status RemoveWatchpoint(lldb::addr_t addr); //---------------------------------------------------------------------- // Accessors //---------------------------------------------------------------------- lldb::pid_t GetID() const { return m_pid; } lldb::StateType GetState() const; bool IsRunning() const { return m_state == lldb::eStateRunning || IsStepping(); } bool IsStepping() const { return m_state == lldb::eStateStepping; } bool CanResume() const { return m_state == lldb::eStateStopped; } bool GetByteOrder(lldb::ByteOrder &byte_order) const; virtual llvm::ErrorOr> GetAuxvData() const = 0; //---------------------------------------------------------------------- // Exit Status //---------------------------------------------------------------------- virtual bool GetExitStatus(lldb_private::ExitType *exit_type, int *status, std::string &exit_description); virtual bool SetExitStatus(lldb_private::ExitType exit_type, int status, const char *exit_description, bool bNotifyStateChange); //---------------------------------------------------------------------- // Access to threads //---------------------------------------------------------------------- NativeThreadProtocolSP GetThreadAtIndex(uint32_t idx); NativeThreadProtocolSP GetThreadByID(lldb::tid_t tid); void SetCurrentThreadID(lldb::tid_t tid) { m_current_thread_id = tid; } lldb::tid_t GetCurrentThreadID() { return m_current_thread_id; } NativeThreadProtocolSP GetCurrentThread() { return GetThreadByID(m_current_thread_id); } //---------------------------------------------------------------------- // Access to inferior stdio //---------------------------------------------------------------------- virtual int GetTerminalFileDescriptor() { return m_terminal_fd; } //---------------------------------------------------------------------- // Stop id interface //---------------------------------------------------------------------- uint32_t GetStopID() const; // --------------------------------------------------------------------- // Callbacks for low-level process state changes // --------------------------------------------------------------------- class NativeDelegate { public: virtual ~NativeDelegate() {} virtual void InitializeDelegate(NativeProcessProtocol *process) = 0; virtual void ProcessStateChanged(NativeProcessProtocol *process, lldb::StateType state) = 0; virtual void DidExec(NativeProcessProtocol *process) = 0; }; //------------------------------------------------------------------ /// Register a native delegate. /// /// Clients can register nofication callbacks by passing in a /// NativeDelegate impl and passing it into this function. /// /// Note: it is required that the lifetime of the /// native_delegate outlive the NativeProcessProtocol. /// /// @param[in] native_delegate /// A NativeDelegate impl to be called when certain events /// happen within the NativeProcessProtocol or related threads. /// /// @return /// true if the delegate was registered successfully; /// false if the delegate was already registered. /// /// @see NativeProcessProtocol::NativeDelegate. //------------------------------------------------------------------ bool RegisterNativeDelegate(NativeDelegate &native_delegate); //------------------------------------------------------------------ /// Unregister a native delegate previously registered. /// /// @param[in] native_delegate /// A NativeDelegate impl previously registered with this process. /// /// @return Returns \b true if the NativeDelegate was /// successfully removed from the process, \b false otherwise. /// /// @see NativeProcessProtocol::NativeDelegate //------------------------------------------------------------------ bool UnregisterNativeDelegate(NativeDelegate &native_delegate); virtual Status GetLoadedModuleFileSpec(const char *module_path, FileSpec &file_spec) = 0; virtual Status GetFileLoadAddress(const llvm::StringRef &file_name, lldb::addr_t &load_addr) = 0; //------------------------------------------------------------------ /// Launch a process for debugging. This method will create an concrete /// instance of NativeProcessProtocol, based on the host platform. /// (e.g. NativeProcessLinux on linux, etc.) /// /// @param[in] launch_info /// Information required to launch the process. /// /// @param[in] native_delegate /// The delegate that will receive messages regarding the /// inferior. Must outlive the NativeProcessProtocol /// instance. /// /// @param[in] mainloop /// The mainloop instance with which the process can register /// callbacks. Must outlive the NativeProcessProtocol /// instance. /// /// @param[out] process_sp /// On successful return from the method, this parameter /// contains the shared pointer to the /// NativeProcessProtocol that can be used to manipulate /// the native process. /// /// @return /// An error object indicating if the operation succeeded, /// and if not, what error occurred. //------------------------------------------------------------------ static Status Launch(ProcessLaunchInfo &launch_info, NativeDelegate &native_delegate, MainLoop &mainloop, NativeProcessProtocolSP &process_sp); //------------------------------------------------------------------ /// Attach to an existing process. This method will create an concrete /// instance of NativeProcessProtocol, based on the host platform. /// (e.g. NativeProcessLinux on linux, etc.) /// /// @param[in] pid /// pid of the process locatable /// /// @param[in] native_delegate /// The delegate that will receive messages regarding the /// inferior. Must outlive the NativeProcessProtocol /// instance. /// /// @param[in] mainloop /// The mainloop instance with which the process can register /// callbacks. Must outlive the NativeProcessProtocol /// instance. /// /// @param[out] process_sp /// On successful return from the method, this parameter /// contains the shared pointer to the /// NativeProcessProtocol that can be used to manipulate /// the native process. /// /// @return /// An error object indicating if the operation succeeded, /// and if not, what error occurred. //------------------------------------------------------------------ static Status Attach(lldb::pid_t pid, NativeDelegate &native_delegate, MainLoop &mainloop, NativeProcessProtocolSP &process_sp); + //------------------------------------------------------------------ + /// StartTracing API for starting a tracing instance with the + /// TraceOptions on a specific thread or process. + /// + /// @param[in] config + /// The configuration to use when starting tracing. + /// + /// @param[out] error + /// Status indicates what went wrong. + /// + /// @return + /// The API returns a user_id which can be used to get trace + /// data, trace configuration or stopping the trace instance. + /// The user_id is a key to identify and operate with a tracing + /// instance. It may refer to the complete process or a single + /// thread. + //------------------------------------------------------------------ + virtual lldb::user_id_t StartTrace(const TraceOptions &config, + Status &error) { + error.SetErrorString("Not implemented"); + return LLDB_INVALID_UID; + } + + //------------------------------------------------------------------ + /// StopTracing API as the name suggests stops a tracing instance. + /// + /// @param[in] uid + /// The user id of the trace intended to be stopped. Now a + /// user_id may map to multiple threads in which case this API + /// could be used to stop the tracing for a specific thread by + /// supplying its thread id. + /// + /// @param[in] thread + /// Thread is needed when the complete process is being traced + /// and the user wishes to stop tracing on a particular thread. + /// + /// @return + /// Status indicating what went wrong. + //------------------------------------------------------------------ + virtual Status StopTrace(lldb::user_id_t uid, + lldb::tid_t thread = LLDB_INVALID_THREAD_ID) { + return Status("Not implemented"); + } + + //------------------------------------------------------------------ + /// This API provides the trace data collected in the form of raw + /// data. + /// + /// @param[in] uid thread + /// The uid and thread provide the context for the trace + /// instance. + /// + /// @param[in] buffer + /// The buffer provides the destination buffer where the trace + /// data would be read to. The buffer should be truncated to the + /// filled length by this function. + /// + /// @param[in] offset + /// There is possibility to read partially the trace data from + /// a specified offset where in such cases the buffer provided + /// may be smaller than the internal trace collection container. + /// + /// @return + /// The size of the data actually read. + //------------------------------------------------------------------ + virtual Status GetData(lldb::user_id_t uid, lldb::tid_t thread, + llvm::MutableArrayRef &buffer, + size_t offset = 0) { + return Status("Not implemented"); + } + + //------------------------------------------------------------------ + /// Similar API as above except it aims to provide any extra data + /// useful for decoding the actual trace data. + //------------------------------------------------------------------ + virtual Status GetMetaData(lldb::user_id_t uid, lldb::tid_t thread, + llvm::MutableArrayRef &buffer, + size_t offset = 0) { + return Status("Not implemented"); + } + + //------------------------------------------------------------------ + /// API to query the TraceOptions for a given user id + /// + /// @param[in] uid + /// The user id of the tracing instance. + /// + /// @param[in] config + /// The thread id of the tracing instance, in case configuration + /// for a specific thread is needed should be specified in the + /// config. + /// + /// @param[out] error + /// Status indicates what went wrong. + /// + /// @param[out] config + /// The actual configuration being used for tracing. + //------------------------------------------------------------------ + virtual Status GetTraceConfig(lldb::user_id_t uid, TraceOptions &config) { + return Status("Not implemented"); + } + protected: lldb::pid_t m_pid; std::vector m_threads; lldb::tid_t m_current_thread_id; mutable std::recursive_mutex m_threads_mutex; lldb::StateType m_state; mutable std::recursive_mutex m_state_mutex; lldb_private::ExitType m_exit_type; int m_exit_status; std::string m_exit_description; std::recursive_mutex m_delegates_mutex; std::vector m_delegates; NativeBreakpointList m_breakpoint_list; NativeWatchpointList m_watchpoint_list; HardwareBreakpointMap m_hw_breakpoints_map; int m_terminal_fd; uint32_t m_stop_id; // Set of signal numbers that LLDB directly injects back to inferior // without stopping it. llvm::DenseSet m_signals_to_ignore; // lldb_private::Host calls should be used to launch a process for debugging, // and // then the process should be attached to. When attaching to a process // lldb_private::Host calls should be used to locate the process to attach to, // and then this function should be called. NativeProcessProtocol(lldb::pid_t pid); // ----------------------------------------------------------- // Internal interface for state handling // ----------------------------------------------------------- void SetState(lldb::StateType state, bool notify_delegates = true); // Derived classes need not implement this. It can be used as a // hook to clear internal caches that should be invalidated when // stop ids change. // // Note this function is called with the state mutex obtained // by the caller. virtual void DoStopIDBumped(uint32_t newBumpId); // ----------------------------------------------------------- // Internal interface for software breakpoints // ----------------------------------------------------------- Status SetSoftwareBreakpoint(lldb::addr_t addr, uint32_t size_hint); virtual Status GetSoftwareBreakpointTrapOpcode(size_t trap_opcode_size_hint, size_t &actual_opcode_size, const uint8_t *&trap_opcode_bytes) = 0; // ----------------------------------------------------------- /// Notify the delegate that an exec occurred. /// /// Provide a mechanism for a delegate to clear out any exec- /// sensitive data. // ----------------------------------------------------------- void NotifyDidExec(); NativeThreadProtocolSP GetThreadByIDUnlocked(lldb::tid_t tid); // ----------------------------------------------------------- // Static helper methods for derived classes. // ----------------------------------------------------------- static Status ResolveProcessArchitecture(lldb::pid_t pid, ArchSpec &arch); private: void SynchronouslyNotifyProcessStateChanged(lldb::StateType state); }; -} +} // namespace lldb_private #endif // #ifndef liblldb_NativeProcessProtocol_h_ Index: vendor/lldb/dist/include/lldb/Target/Process.h =================================================================== --- vendor/lldb/dist/include/lldb/Target/Process.h (revision 319149) +++ vendor/lldb/dist/include/lldb/Target/Process.h (revision 319150) @@ -1,3256 +1,3251 @@ //===-- Process.h -----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_Process_h_ #define liblldb_Process_h_ #include "lldb/Host/Config.h" // C Includes #include // C++ Includes #include #include #include #include #include #include #include // Other libraries and framework includes // Project includes #include "lldb/Breakpoint/BreakpointSiteList.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Broadcaster.h" #include "lldb/Core/Communication.h" #include "lldb/Core/Event.h" #include "lldb/Core/Listener.h" #include "lldb/Core/LoadedModuleInfoList.h" #include "lldb/Core/PluginInterface.h" #include "lldb/Core/StructuredData.h" #include "lldb/Core/ThreadSafeValue.h" #include "lldb/Core/TraceOptions.h" #include "lldb/Core/UserSettingsController.h" #include "lldb/Host/HostThread.h" #include "lldb/Host/ProcessRunLock.h" #include "lldb/Interpreter/Options.h" #include "lldb/Target/ExecutionContextScope.h" #include "lldb/Target/InstrumentationRuntime.h" #include "lldb/Target/Memory.h" #include "lldb/Target/ProcessInfo.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "lldb/Target/QueueList.h" #include "lldb/Target/ThreadList.h" #include "lldb/Utility/NameMatches.h" #include "lldb/Utility/Status.h" #include "lldb/lldb-private.h" #include "llvm/ADT/ArrayRef.h" namespace lldb_private { template struct Range; //---------------------------------------------------------------------- // ProcessProperties //---------------------------------------------------------------------- class ProcessProperties : public Properties { public: // Pass nullptr for "process" if the ProcessProperties are to be the global // copy ProcessProperties(lldb_private::Process *process); ~ProcessProperties() override; bool GetDisableMemoryCache() const; uint64_t GetMemoryCacheLineSize() const; Args GetExtraStartupCommands() const; void SetExtraStartupCommands(const Args &args); FileSpec GetPythonOSPluginPath() const; void SetPythonOSPluginPath(const FileSpec &file); bool GetIgnoreBreakpointsInExpressions() const; void SetIgnoreBreakpointsInExpressions(bool ignore); bool GetUnwindOnErrorInExpressions() const; void SetUnwindOnErrorInExpressions(bool ignore); bool GetStopOnSharedLibraryEvents() const; void SetStopOnSharedLibraryEvents(bool stop); bool GetDetachKeepsStopped() const; void SetDetachKeepsStopped(bool keep_stopped); bool GetWarningsOptimization() const; protected: static void OptionValueChangedCallback(void *baton, OptionValue *option_value); Process *m_process; // Can be nullptr for global ProcessProperties }; typedef std::shared_ptr ProcessPropertiesSP; //---------------------------------------------------------------------- // ProcessInstanceInfo // // Describes an existing process and any discoverable information that // pertains to that process. //---------------------------------------------------------------------- class ProcessInstanceInfo : public ProcessInfo { public: ProcessInstanceInfo() : ProcessInfo(), m_euid(UINT32_MAX), m_egid(UINT32_MAX), m_parent_pid(LLDB_INVALID_PROCESS_ID) {} ProcessInstanceInfo(const char *name, const ArchSpec &arch, lldb::pid_t pid) : ProcessInfo(name, arch, pid), m_euid(UINT32_MAX), m_egid(UINT32_MAX), m_parent_pid(LLDB_INVALID_PROCESS_ID) {} void Clear() { ProcessInfo::Clear(); m_euid = UINT32_MAX; m_egid = UINT32_MAX; m_parent_pid = LLDB_INVALID_PROCESS_ID; } uint32_t GetEffectiveUserID() const { return m_euid; } uint32_t GetEffectiveGroupID() const { return m_egid; } bool EffectiveUserIDIsValid() const { return m_euid != UINT32_MAX; } bool EffectiveGroupIDIsValid() const { return m_egid != UINT32_MAX; } void SetEffectiveUserID(uint32_t uid) { m_euid = uid; } void SetEffectiveGroupID(uint32_t gid) { m_egid = gid; } lldb::pid_t GetParentProcessID() const { return m_parent_pid; } void SetParentProcessID(lldb::pid_t pid) { m_parent_pid = pid; } bool ParentProcessIDIsValid() const { return m_parent_pid != LLDB_INVALID_PROCESS_ID; } void Dump(Stream &s, Platform *platform) const; static void DumpTableHeader(Stream &s, Platform *platform, bool show_args, bool verbose); void DumpAsTableRow(Stream &s, Platform *platform, bool show_args, bool verbose) const; protected: uint32_t m_euid; uint32_t m_egid; lldb::pid_t m_parent_pid; }; //---------------------------------------------------------------------- // ProcessAttachInfo // // Describes any information that is required to attach to a process. //---------------------------------------------------------------------- class ProcessAttachInfo : public ProcessInstanceInfo { public: ProcessAttachInfo() : ProcessInstanceInfo(), m_listener_sp(), m_hijack_listener_sp(), m_plugin_name(), m_resume_count(0), m_wait_for_launch(false), m_ignore_existing(true), m_continue_once_attached(false), m_detach_on_error(true), m_async(false) {} ProcessAttachInfo(const ProcessLaunchInfo &launch_info) : ProcessInstanceInfo(), m_listener_sp(), m_hijack_listener_sp(), m_plugin_name(), m_resume_count(0), m_wait_for_launch(false), m_ignore_existing(true), m_continue_once_attached(false), m_detach_on_error(true), m_async(false) { ProcessInfo::operator=(launch_info); SetProcessPluginName(launch_info.GetProcessPluginName()); SetResumeCount(launch_info.GetResumeCount()); SetListener(launch_info.GetListener()); SetHijackListener(launch_info.GetHijackListener()); m_detach_on_error = launch_info.GetDetachOnError(); } bool GetWaitForLaunch() const { return m_wait_for_launch; } void SetWaitForLaunch(bool b) { m_wait_for_launch = b; } bool GetAsync() const { return m_async; } void SetAsync(bool b) { m_async = b; } bool GetIgnoreExisting() const { return m_ignore_existing; } void SetIgnoreExisting(bool b) { m_ignore_existing = b; } bool GetContinueOnceAttached() const { return m_continue_once_attached; } void SetContinueOnceAttached(bool b) { m_continue_once_attached = b; } uint32_t GetResumeCount() const { return m_resume_count; } void SetResumeCount(uint32_t c) { m_resume_count = c; } const char *GetProcessPluginName() const { return (m_plugin_name.empty() ? nullptr : m_plugin_name.c_str()); } void SetProcessPluginName(llvm::StringRef plugin) { m_plugin_name = plugin; } void Clear() { ProcessInstanceInfo::Clear(); m_plugin_name.clear(); m_resume_count = 0; m_wait_for_launch = false; m_ignore_existing = true; m_continue_once_attached = false; } bool ProcessInfoSpecified() const { if (GetExecutableFile()) return true; if (GetProcessID() != LLDB_INVALID_PROCESS_ID) return true; if (GetParentProcessID() != LLDB_INVALID_PROCESS_ID) return true; return false; } lldb::ListenerSP GetHijackListener() const { return m_hijack_listener_sp; } void SetHijackListener(const lldb::ListenerSP &listener_sp) { m_hijack_listener_sp = listener_sp; } bool GetDetachOnError() const { return m_detach_on_error; } void SetDetachOnError(bool enable) { m_detach_on_error = enable; } // Get and set the actual listener that will be used for the process events lldb::ListenerSP GetListener() const { return m_listener_sp; } void SetListener(const lldb::ListenerSP &listener_sp) { m_listener_sp = listener_sp; } lldb::ListenerSP GetListenerForProcess(Debugger &debugger); protected: lldb::ListenerSP m_listener_sp; lldb::ListenerSP m_hijack_listener_sp; std::string m_plugin_name; uint32_t m_resume_count; // How many times do we resume after launching bool m_wait_for_launch; bool m_ignore_existing; bool m_continue_once_attached; // Supports the use-case scenario of // immediately continuing the process once // attached. bool m_detach_on_error; // If we are debugging remotely, instruct the stub to // detach rather than killing the target on error. bool m_async; // Use an async attach where we start the attach and return // immediately (used by GUI programs with --waitfor so they can // call SBProcess::Stop() to cancel attach) }; class ProcessLaunchCommandOptions : public Options { public: ProcessLaunchCommandOptions() : Options() { // Keep default values of all options in one place: OptionParsingStarting () OptionParsingStarting(nullptr); } ~ProcessLaunchCommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override; void OptionParsingStarting(ExecutionContext *execution_context) override { launch_info.Clear(); disable_aslr = eLazyBoolCalculate; } llvm::ArrayRef GetDefinitions() override; // Instance variables to hold the values for command options. ProcessLaunchInfo launch_info; lldb_private::LazyBool disable_aslr; }; //---------------------------------------------------------------------- // ProcessInstanceInfoMatch // // A class to help matching one ProcessInstanceInfo to another. //---------------------------------------------------------------------- class ProcessInstanceInfoMatch { public: ProcessInstanceInfoMatch() : m_match_info(), m_name_match_type(NameMatch::Ignore), m_match_all_users(false) {} ProcessInstanceInfoMatch(const char *process_name, NameMatch process_name_match_type) : m_match_info(), m_name_match_type(process_name_match_type), m_match_all_users(false) { m_match_info.GetExecutableFile().SetFile(process_name, false); } ProcessInstanceInfo &GetProcessInfo() { return m_match_info; } const ProcessInstanceInfo &GetProcessInfo() const { return m_match_info; } bool GetMatchAllUsers() const { return m_match_all_users; } void SetMatchAllUsers(bool b) { m_match_all_users = b; } NameMatch GetNameMatchType() const { return m_name_match_type; } void SetNameMatchType(NameMatch name_match_type) { m_name_match_type = name_match_type; } bool NameMatches(const char *process_name) const; bool Matches(const ProcessInstanceInfo &proc_info) const; bool MatchAllProcesses() const; void Clear(); protected: ProcessInstanceInfo m_match_info; NameMatch m_name_match_type; bool m_match_all_users; }; class ProcessInstanceInfoList { public: ProcessInstanceInfoList() = default; void Clear() { m_infos.clear(); } size_t GetSize() { return m_infos.size(); } void Append(const ProcessInstanceInfo &info) { m_infos.push_back(info); } const char *GetProcessNameAtIndex(size_t idx) { return ((idx < m_infos.size()) ? m_infos[idx].GetName() : nullptr); } size_t GetProcessNameLengthAtIndex(size_t idx) { return ((idx < m_infos.size()) ? m_infos[idx].GetNameLength() : 0); } lldb::pid_t GetProcessIDAtIndex(size_t idx) { return ((idx < m_infos.size()) ? m_infos[idx].GetProcessID() : 0); } bool GetInfoAtIndex(size_t idx, ProcessInstanceInfo &info) { if (idx < m_infos.size()) { info = m_infos[idx]; return true; } return false; } // You must ensure "idx" is valid before calling this function const ProcessInstanceInfo &GetProcessInfoAtIndex(size_t idx) const { assert(idx < m_infos.size()); return m_infos[idx]; } protected: typedef std::vector collection; collection m_infos; }; // This class tracks the Modification state of the process. Things that can // currently modify // the program are running the program (which will up the StopID) and writing // memory (which // will up the MemoryID.) // FIXME: Should we also include modification of register states? class ProcessModID { friend bool operator==(const ProcessModID &lhs, const ProcessModID &rhs); public: ProcessModID() : m_stop_id(0), m_last_natural_stop_id(0), m_resume_id(0), m_memory_id(0), m_last_user_expression_resume(0), m_running_user_expression(false) {} ProcessModID(const ProcessModID &rhs) : m_stop_id(rhs.m_stop_id), m_memory_id(rhs.m_memory_id) {} const ProcessModID &operator=(const ProcessModID &rhs) { if (this != &rhs) { m_stop_id = rhs.m_stop_id; m_memory_id = rhs.m_memory_id; } return *this; } ~ProcessModID() = default; void BumpStopID() { m_stop_id++; if (!IsLastResumeForUserExpression()) m_last_natural_stop_id++; } void BumpMemoryID() { m_memory_id++; } void BumpResumeID() { m_resume_id++; if (m_running_user_expression > 0) m_last_user_expression_resume = m_resume_id; } uint32_t GetStopID() const { return m_stop_id; } uint32_t GetLastNaturalStopID() const { return m_last_natural_stop_id; } uint32_t GetMemoryID() const { return m_memory_id; } uint32_t GetResumeID() const { return m_resume_id; } uint32_t GetLastUserExpressionResumeID() const { return m_last_user_expression_resume; } bool MemoryIDEqual(const ProcessModID &compare) const { return m_memory_id == compare.m_memory_id; } bool StopIDEqual(const ProcessModID &compare) const { return m_stop_id == compare.m_stop_id; } void SetInvalid() { m_stop_id = UINT32_MAX; } bool IsValid() const { return m_stop_id != UINT32_MAX; } bool IsLastResumeForUserExpression() const { // If we haven't yet resumed the target, then it can't be for a user // expression... if (m_resume_id == 0) return false; return m_resume_id == m_last_user_expression_resume; } void SetRunningUserExpression(bool on) { if (on) m_running_user_expression++; else m_running_user_expression--; } void SetStopEventForLastNaturalStopID(lldb::EventSP event_sp) { m_last_natural_stop_event = event_sp; } lldb::EventSP GetStopEventForStopID(uint32_t stop_id) const { if (stop_id == m_last_natural_stop_id) return m_last_natural_stop_event; return lldb::EventSP(); } private: uint32_t m_stop_id; uint32_t m_last_natural_stop_id; uint32_t m_resume_id; uint32_t m_memory_id; uint32_t m_last_user_expression_resume; uint32_t m_running_user_expression; lldb::EventSP m_last_natural_stop_event; }; inline bool operator==(const ProcessModID &lhs, const ProcessModID &rhs) { if (lhs.StopIDEqual(rhs) && lhs.MemoryIDEqual(rhs)) return true; else return false; } inline bool operator!=(const ProcessModID &lhs, const ProcessModID &rhs) { return (!lhs.StopIDEqual(rhs) || !lhs.MemoryIDEqual(rhs)); } //---------------------------------------------------------------------- /// @class Process Process.h "lldb/Target/Process.h" /// @brief A plug-in interface definition class for debugging a process. //---------------------------------------------------------------------- class Process : public std::enable_shared_from_this, public ProcessProperties, public UserID, public Broadcaster, public ExecutionContextScope, public PluginInterface { friend class FunctionCaller; // For WaitForStateChangeEventsPrivate friend class Debugger; // For PopProcessIOHandler and ProcessIOHandlerIsActive friend class DynamicLoader; // For LoadOperatingSystemPlugin friend class ProcessEventData; friend class StopInfo; friend class Target; friend class ThreadList; public: //------------------------------------------------------------------ /// Broadcaster event bits definitions. //------------------------------------------------------------------ enum { eBroadcastBitStateChanged = (1 << 0), eBroadcastBitInterrupt = (1 << 1), eBroadcastBitSTDOUT = (1 << 2), eBroadcastBitSTDERR = (1 << 3), eBroadcastBitProfileData = (1 << 4), eBroadcastBitStructuredData = (1 << 5), }; enum { eBroadcastInternalStateControlStop = (1 << 0), eBroadcastInternalStateControlPause = (1 << 1), eBroadcastInternalStateControlResume = (1 << 2) }; //------------------------------------------------------------------ /// Process warning types. //------------------------------------------------------------------ enum Warnings { eWarningsOptimization = 1 }; typedef Range LoadRange; // We use a read/write lock to allow on or more clients to // access the process state while the process is stopped (reader). // We lock the write lock to control access to the process // while it is running (readers, or clients that want the process // stopped can block waiting for the process to stop, or just // try to lock it to see if they can immediately access the stopped // process. If the try read lock fails, then the process is running. typedef ProcessRunLock::ProcessRunLocker StopLocker; // These two functions fill out the Broadcaster interface: static ConstString &GetStaticBroadcasterClass(); ConstString &GetBroadcasterClass() const override { return GetStaticBroadcasterClass(); } //------------------------------------------------------------------ /// A notification structure that can be used by clients to listen /// for changes in a process's lifetime. /// /// @see RegisterNotificationCallbacks (const Notifications&) /// @see UnregisterNotificationCallbacks (const Notifications&) //------------------------------------------------------------------ #ifndef SWIG typedef struct { void *baton; void (*initialize)(void *baton, Process *process); void (*process_state_changed)(void *baton, Process *process, lldb::StateType state); } Notifications; class ProcessEventData : public EventData { friend class Process; public: ProcessEventData(); ProcessEventData(const lldb::ProcessSP &process, lldb::StateType state); ~ProcessEventData() override; static const ConstString &GetFlavorString(); const ConstString &GetFlavor() const override; lldb::ProcessSP GetProcessSP() const { return m_process_wp.lock(); } lldb::StateType GetState() const { return m_state; } bool GetRestarted() const { return m_restarted; } size_t GetNumRestartedReasons() { return m_restarted_reasons.size(); } const char *GetRestartedReasonAtIndex(size_t idx) { return ((idx < m_restarted_reasons.size()) ? m_restarted_reasons[idx].c_str() : nullptr); } bool GetInterrupted() const { return m_interrupted; } void Dump(Stream *s) const override; void DoOnRemoval(Event *event_ptr) override; static const Process::ProcessEventData * GetEventDataFromEvent(const Event *event_ptr); static lldb::ProcessSP GetProcessFromEvent(const Event *event_ptr); static lldb::StateType GetStateFromEvent(const Event *event_ptr); static bool GetRestartedFromEvent(const Event *event_ptr); static size_t GetNumRestartedReasons(const Event *event_ptr); static const char *GetRestartedReasonAtIndex(const Event *event_ptr, size_t idx); static void AddRestartedReason(Event *event_ptr, const char *reason); static void SetRestartedInEvent(Event *event_ptr, bool new_value); static bool GetInterruptedFromEvent(const Event *event_ptr); static void SetInterruptedInEvent(Event *event_ptr, bool new_value); static bool SetUpdateStateOnRemoval(Event *event_ptr); private: void SetUpdateStateOnRemoval() { m_update_state++; } void SetRestarted(bool new_value) { m_restarted = new_value; } void SetInterrupted(bool new_value) { m_interrupted = new_value; } void AddRestartedReason(const char *reason) { m_restarted_reasons.push_back(reason); } lldb::ProcessWP m_process_wp; lldb::StateType m_state; std::vector m_restarted_reasons; bool m_restarted; // For "eStateStopped" events, this is true if the target // was automatically restarted. int m_update_state; bool m_interrupted; DISALLOW_COPY_AND_ASSIGN(ProcessEventData); }; #endif // SWIG //------------------------------------------------------------------ /// Construct with a shared pointer to a target, and the Process listener. /// Uses the Host UnixSignalsSP by default. //------------------------------------------------------------------ Process(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); //------------------------------------------------------------------ /// Construct with a shared pointer to a target, the Process listener, /// and the appropriate UnixSignalsSP for the process. //------------------------------------------------------------------ Process(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const lldb::UnixSignalsSP &unix_signals_sp); //------------------------------------------------------------------ /// Destructor. /// /// The destructor is virtual since this class is designed to be /// inherited from by the plug-in instance. //------------------------------------------------------------------ ~Process() override; static void SettingsInitialize(); static void SettingsTerminate(); static const ProcessPropertiesSP &GetGlobalProperties(); //------------------------------------------------------------------ /// Find a Process plug-in that can debug \a module using the /// currently selected architecture. /// /// Scans all loaded plug-in interfaces that implement versions of /// the Process plug-in interface and returns the first instance /// that can debug the file. /// /// @param[in] module_sp /// The module shared pointer that this process will debug. /// /// @param[in] plugin_name /// If nullptr, select the best plug-in for the binary. If non-nullptr /// then look for a plugin whose PluginInfo's name matches /// this string. /// /// @see Process::CanDebug () //------------------------------------------------------------------ static lldb::ProcessSP FindPlugin(lldb::TargetSP target_sp, llvm::StringRef plugin_name, lldb::ListenerSP listener_sp, const FileSpec *crash_file_path); //------------------------------------------------------------------ /// Static function that can be used with the \b host function /// Host::StartMonitoringChildProcess (). /// /// This function can be used by lldb_private::Process subclasses /// when they want to watch for a local process and have its exit /// status automatically set when the host child process exits. /// Subclasses should call Host::StartMonitoringChildProcess () /// with: /// callback = Process::SetHostProcessExitStatus /// pid = Process::GetID() /// monitor_signals = false //------------------------------------------------------------------ static bool SetProcessExitStatus(lldb::pid_t pid, // The process ID we want to monitor bool exited, int signo, // Zero for no signal int status); // Exit value of process if signal is zero lldb::ByteOrder GetByteOrder() const; uint32_t GetAddressByteSize() const; uint32_t GetUniqueID() const { return m_process_unique_id; } //------------------------------------------------------------------ /// Check if a plug-in instance can debug the file in \a module. /// /// Each plug-in is given a chance to say whether it can debug /// the file in \a module. If the Process plug-in instance can /// debug a file on the current system, it should return \b true. /// /// @return /// Returns \b true if this Process plug-in instance can /// debug the executable, \b false otherwise. //------------------------------------------------------------------ virtual bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) = 0; //------------------------------------------------------------------ /// This object is about to be destroyed, do any necessary cleanup. /// /// Subclasses that override this method should always call this /// superclass method. //------------------------------------------------------------------ virtual void Finalize(); //------------------------------------------------------------------ /// Return whether this object is valid (i.e. has not been finalized.) /// /// @return /// Returns \b true if this Process has not been finalized /// and \b false otherwise. //------------------------------------------------------------------ bool IsValid() const { return !m_finalize_called; } //------------------------------------------------------------------ /// Return a multi-word command object that can be used to expose /// plug-in specific commands. /// /// This object will be used to resolve plug-in commands and can be /// triggered by a call to: /// /// (lldb) process commmand /// /// @return /// A CommandObject which can be one of the concrete subclasses /// of CommandObject like CommandObjectRaw, CommandObjectParsed, /// or CommandObjectMultiword. //------------------------------------------------------------------ virtual CommandObject *GetPluginCommandObject() { return nullptr; } //------------------------------------------------------------------ /// Launch a new process. /// /// Launch a new process by spawning a new process using the /// target object's executable module's file as the file to launch. /// /// This function is not meant to be overridden by Process /// subclasses. It will first call Process::WillLaunch (Module *) /// and if that returns \b true, Process::DoLaunch (Module*, /// char const *[],char const *[],const char *,const char *, /// const char *) will be called to actually do the launching. If /// DoLaunch returns \b true, then Process::DidLaunch() will be /// called. /// /// @param[in] launch_info /// Details regarding the environment, STDIN/STDOUT/STDERR /// redirection, working path, etc. related to the requested launch. /// /// @return /// An error object. Call GetID() to get the process ID if /// the error object is success. //------------------------------------------------------------------ virtual Status Launch(ProcessLaunchInfo &launch_info); virtual Status LoadCore(); virtual Status DoLoadCore() { Status error; error.SetErrorStringWithFormat( "error: %s does not support loading core files.", GetPluginName().GetCString()); return error; } //------------------------------------------------------------------ /// Get the dynamic loader plug-in for this process. /// /// The default action is to let the DynamicLoader plug-ins check /// the main executable and the DynamicLoader will select itself /// automatically. Subclasses can override this if inspecting the /// executable is not desired, or if Process subclasses can only /// use a specific DynamicLoader plug-in. //------------------------------------------------------------------ virtual DynamicLoader *GetDynamicLoader(); //------------------------------------------------------------------ // Returns AUXV structure found in many ELF-based environments. // // The default action is to return an empty data buffer. // // @return // A data buffer containing the contents of the AUXV data. //------------------------------------------------------------------ virtual const lldb::DataBufferSP GetAuxvData(); //------------------------------------------------------------------ /// Sometimes processes know how to retrieve and load shared libraries. /// This is normally done by DynamicLoader plug-ins, but sometimes the /// connection to the process allows retrieving this information. The /// dynamic loader plug-ins can use this function if they can't /// determine the current shared library load state. /// /// @return /// The number of shared libraries that were loaded //------------------------------------------------------------------ virtual size_t LoadModules() { return 0; } virtual size_t LoadModules(LoadedModuleInfoList &) { return 0; } protected: virtual JITLoaderList &GetJITLoaders(); public: //------------------------------------------------------------------ /// Get the system runtime plug-in for this process. /// /// @return /// Returns a pointer to the SystemRuntime plugin for this Process /// if one is available. Else returns nullptr. //------------------------------------------------------------------ virtual SystemRuntime *GetSystemRuntime(); //------------------------------------------------------------------ /// Attach to an existing process using the process attach info. /// /// This function is not meant to be overridden by Process /// subclasses. It will first call WillAttach (lldb::pid_t) /// or WillAttach (const char *), and if that returns \b /// true, DoAttach (lldb::pid_t) or DoAttach (const char *) will /// be called to actually do the attach. If DoAttach returns \b /// true, then Process::DidAttach() will be called. /// /// @param[in] pid /// The process ID that we should attempt to attach to. /// /// @return /// Returns \a pid if attaching was successful, or /// LLDB_INVALID_PROCESS_ID if attaching fails. //------------------------------------------------------------------ virtual Status Attach(ProcessAttachInfo &attach_info); //------------------------------------------------------------------ /// Attach to a remote system via a URL /// /// @param[in] strm /// A stream where output intended for the user /// (if the driver has a way to display that) generated during /// the connection. This may be nullptr if no output is needed.A /// /// @param[in] remote_url /// The URL format that we are connecting to. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Status ConnectRemote(Stream *strm, llvm::StringRef remote_url); bool GetShouldDetach() const { return m_should_detach; } void SetShouldDetach(bool b) { m_should_detach = b; } //------------------------------------------------------------------ /// Get the image information address for the current process. /// /// Some runtimes have system functions that can help dynamic /// loaders locate the dynamic loader information needed to observe /// shared libraries being loaded or unloaded. This function is /// in the Process interface (as opposed to the DynamicLoader /// interface) to ensure that remote debugging can take advantage of /// this functionality. /// /// @return /// The address of the dynamic loader information, or /// LLDB_INVALID_ADDRESS if this is not supported by this /// interface. //------------------------------------------------------------------ virtual lldb::addr_t GetImageInfoAddress(); //------------------------------------------------------------------ /// Called when the process is about to broadcast a public stop. /// /// There are public and private stops. Private stops are when the /// process is doing things like stepping and the client doesn't /// need to know about starts and stop that implement a thread plan. /// Single stepping over a source line in code might end up being /// implemented by one or more process starts and stops. Public stops /// are when clients will be notified that the process is stopped. /// These events typically trigger UI updates (thread stack frames to /// be displayed, variables to be displayed, and more). This function /// can be overriden and allows process subclasses to do something /// before the eBroadcastBitStateChanged event is sent to public /// clients. //------------------------------------------------------------------ virtual void WillPublicStop() {} //------------------------------------------------------------------ /// Register for process and thread notifications. /// /// Clients can register notification callbacks by filling out a /// Process::Notifications structure and calling this function. /// /// @param[in] callbacks /// A structure that contains the notification baton and /// callback functions. /// /// @see Process::Notifications //------------------------------------------------------------------ #ifndef SWIG void RegisterNotificationCallbacks(const Process::Notifications &callbacks); #endif //------------------------------------------------------------------ /// Unregister for process and thread notifications. /// /// Clients can unregister notification callbacks by passing a copy of /// the original baton and callbacks in \a callbacks. /// /// @param[in] callbacks /// A structure that contains the notification baton and /// callback functions. /// /// @return /// Returns \b true if the notification callbacks were /// successfully removed from the process, \b false otherwise. /// /// @see Process::Notifications //------------------------------------------------------------------ #ifndef SWIG bool UnregisterNotificationCallbacks(const Process::Notifications &callbacks); #endif //================================================================== // Built in Process Control functions //================================================================== //------------------------------------------------------------------ /// Resumes all of a process's threads as configured using the /// Thread run control functions. /// /// Threads for a process should be updated with one of the run /// control actions (resume, step, or suspend) that they should take /// when the process is resumed. If no run control action is given /// to a thread it will be resumed by default. /// /// This function is not meant to be overridden by Process /// subclasses. This function will take care of disabling any /// breakpoints that threads may be stopped at, single stepping, and /// re-enabling breakpoints, and enabling the basic flow control /// that the plug-in instances need not worry about. /// /// N.B. This function also sets the Write side of the Run Lock, /// which is unset when the corresponding stop event is pulled off /// the Public Event Queue. If you need to resume the process without /// setting the Run Lock, use PrivateResume (though you should only do /// that from inside the Process class. /// /// @return /// Returns an error object. /// /// @see Thread:Resume() /// @see Thread:Step() /// @see Thread:Suspend() //------------------------------------------------------------------ Status Resume(); Status ResumeSynchronous(Stream *stream); //------------------------------------------------------------------ /// Halts a running process. /// /// This function is not meant to be overridden by Process /// subclasses. /// If the process is successfully halted, a eStateStopped /// process event with GetInterrupted will be broadcast. If false, we will /// halt the process with no events generated by the halt. /// /// @param[in] clear_thread_plans /// If true, when the process stops, clear all thread plans. /// /// @param[in] use_run_lock /// Whether to release the run lock after the stop. /// /// @return /// Returns an error object. If the error is empty, the process is /// halted. /// otherwise the halt has failed. //------------------------------------------------------------------ Status Halt(bool clear_thread_plans = false, bool use_run_lock = true); //------------------------------------------------------------------ /// Detaches from a running or stopped process. /// /// This function is not meant to be overridden by Process /// subclasses. /// /// @param[in] keep_stopped /// If true, don't resume the process on detach. /// /// @return /// Returns an error object. //------------------------------------------------------------------ Status Detach(bool keep_stopped); //------------------------------------------------------------------ /// Kills the process and shuts down all threads that were spawned /// to track and monitor the process. /// /// This function is not meant to be overridden by Process /// subclasses. /// /// @param[in] force_kill /// Whether lldb should force a kill (instead of a detach) from /// the inferior process. Normally if lldb launched a binary and /// Destory is called, lldb kills it. If lldb attached to a /// running process and Destory is called, lldb detaches. If /// this behavior needs to be over-ridden, this is the bool that /// can be used. /// /// @return /// Returns an error object. //------------------------------------------------------------------ Status Destroy(bool force_kill); //------------------------------------------------------------------ /// Sends a process a UNIX signal \a signal. /// /// This function is not meant to be overridden by Process /// subclasses. /// /// @return /// Returns an error object. //------------------------------------------------------------------ Status Signal(int signal); void SetUnixSignals(lldb::UnixSignalsSP &&signals_sp); const lldb::UnixSignalsSP &GetUnixSignals(); //================================================================== // Plug-in Process Control Overrides //================================================================== //------------------------------------------------------------------ /// Called before attaching to a process. /// /// Allow Process plug-ins to execute some code before attaching a /// process. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Status WillAttachToProcessWithID(lldb::pid_t pid) { return Status(); } //------------------------------------------------------------------ /// Called before attaching to a process. /// /// Allow Process plug-ins to execute some code before attaching a /// process. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Status WillAttachToProcessWithName(const char *process_name, bool wait_for_launch) { return Status(); } //------------------------------------------------------------------ /// Attach to a remote system via a URL /// /// @param[in] strm /// A stream where output intended for the user /// (if the driver has a way to display that) generated during /// the connection. This may be nullptr if no output is needed.A /// /// @param[in] remote_url /// The URL format that we are connecting to. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Status DoConnectRemote(Stream *strm, llvm::StringRef remote_url) { Status error; error.SetErrorString("remote connections are not supported"); return error; } //------------------------------------------------------------------ /// Attach to an existing process using a process ID. /// /// @param[in] pid /// The process ID that we should attempt to attach to. /// /// @param[in] attach_info /// Information on how to do the attach. For example, GetUserID() /// will return the uid to attach as. /// /// @return /// Returns a successful Status attaching was successful, or /// an appropriate (possibly platform-specific) error code if /// attaching fails. /// hanming : need flag //------------------------------------------------------------------ virtual Status DoAttachToProcessWithID(lldb::pid_t pid, const ProcessAttachInfo &attach_info) { Status error; error.SetErrorStringWithFormat( "error: %s does not support attaching to a process by pid", GetPluginName().GetCString()); return error; } //------------------------------------------------------------------ /// Attach to an existing process using a partial process name. /// /// @param[in] process_name /// The name of the process to attach to. /// /// @param[in] attach_info /// Information on how to do the attach. For example, GetUserID() /// will return the uid to attach as. /// /// @return /// Returns a successful Status attaching was successful, or /// an appropriate (possibly platform-specific) error code if /// attaching fails. //------------------------------------------------------------------ virtual Status DoAttachToProcessWithName(const char *process_name, const ProcessAttachInfo &attach_info) { Status error; error.SetErrorString("attach by name is not supported"); return error; } //------------------------------------------------------------------ /// Called after attaching a process. /// /// @param[in] process_arch /// If you can figure out the process architecture after attach, fill it /// in here. /// /// Allow Process plug-ins to execute some code after attaching to /// a process. //------------------------------------------------------------------ virtual void DidAttach(ArchSpec &process_arch) { process_arch.Clear(); } //------------------------------------------------------------------ /// Called after a process re-execs itself. /// /// Allow Process plug-ins to execute some code after a process has /// exec'ed itself. Subclasses typically should override DoDidExec() /// as the lldb_private::Process class needs to remove its dynamic /// loader, runtime, ABI and other plug-ins, as well as unload all /// shared libraries. //------------------------------------------------------------------ virtual void DidExec(); //------------------------------------------------------------------ /// Subclasses of Process should implement this function if they /// need to do anything after a process exec's itself. //------------------------------------------------------------------ virtual void DoDidExec() {} //------------------------------------------------------------------ /// Called before launching to a process. /// /// Allow Process plug-ins to execute some code before launching a /// process. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Status WillLaunch(Module *module) { return Status(); } //------------------------------------------------------------------ /// Launch a new process. /// /// Launch a new process by spawning a new process using /// \a exe_module's file as the file to launch. Launch details are /// provided in \a launch_info. /// /// @param[in] exe_module /// The module from which to extract the file specification and /// launch. /// /// @param[in] launch_info /// Details (e.g. arguments, stdio redirection, etc.) for the /// requested launch. /// /// @return /// An Status instance indicating success or failure of the /// operation. //------------------------------------------------------------------ virtual Status DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) { Status error; error.SetErrorStringWithFormat( "error: %s does not support launching processes", GetPluginName().GetCString()); return error; } //------------------------------------------------------------------ /// Called after launching a process. /// /// Allow Process plug-ins to execute some code after launching /// a process. //------------------------------------------------------------------ virtual void DidLaunch() {} //------------------------------------------------------------------ /// Called before resuming to a process. /// /// Allow Process plug-ins to execute some code before resuming a /// process. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Status WillResume() { return Status(); } //------------------------------------------------------------------ /// Resumes all of a process's threads as configured using the /// Thread run control functions. /// /// Threads for a process should be updated with one of the run /// control actions (resume, step, or suspend) that they should take /// when the process is resumed. If no run control action is given /// to a thread it will be resumed by default. /// /// @return /// Returns \b true if the process successfully resumes using /// the thread run control actions, \b false otherwise. /// /// @see Thread:Resume() /// @see Thread:Step() /// @see Thread:Suspend() //------------------------------------------------------------------ virtual Status DoResume() { Status error; error.SetErrorStringWithFormat( "error: %s does not support resuming processes", GetPluginName().GetCString()); return error; } //------------------------------------------------------------------ /// Called after resuming a process. /// /// Allow Process plug-ins to execute some code after resuming /// a process. //------------------------------------------------------------------ virtual void DidResume() {} //------------------------------------------------------------------ /// Called before halting to a process. /// /// Allow Process plug-ins to execute some code before halting a /// process. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Status WillHalt() { return Status(); } //------------------------------------------------------------------ /// Halts a running process. /// /// DoHalt must produce one and only one stop StateChanged event if it /// actually /// stops the process. If the stop happens through some natural event (for /// instance a SIGSTOP), then forwarding that event will do. Otherwise, you /// must /// generate the event manually. This function is called from the context of /// the /// private state thread. /// /// @param[out] caused_stop /// If true, then this Halt caused the stop, otherwise, the /// process was already stopped. /// /// @return /// Returns \b true if the process successfully halts, \b false /// otherwise. //------------------------------------------------------------------ virtual Status DoHalt(bool &caused_stop) { Status error; error.SetErrorStringWithFormat( "error: %s does not support halting processes", GetPluginName().GetCString()); return error; } //------------------------------------------------------------------ /// Called after halting a process. /// /// Allow Process plug-ins to execute some code after halting /// a process. //------------------------------------------------------------------ virtual void DidHalt() {} //------------------------------------------------------------------ /// Called before detaching from a process. /// /// Allow Process plug-ins to execute some code before detaching /// from a process. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Status WillDetach() { return Status(); } //------------------------------------------------------------------ /// Detaches from a running or stopped process. /// /// @return /// Returns \b true if the process successfully detaches, \b /// false otherwise. //------------------------------------------------------------------ virtual Status DoDetach(bool keep_stopped) { Status error; error.SetErrorStringWithFormat( "error: %s does not support detaching from processes", GetPluginName().GetCString()); return error; } //------------------------------------------------------------------ /// Called after detaching from a process. /// /// Allow Process plug-ins to execute some code after detaching /// from a process. //------------------------------------------------------------------ virtual void DidDetach() {} virtual bool DetachRequiresHalt() { return false; } //------------------------------------------------------------------ /// Called before sending a signal to a process. /// /// Allow Process plug-ins to execute some code before sending a /// signal to a process. /// /// @return /// Returns no error if it is safe to proceed with a call to /// Process::DoSignal(int), otherwise an error describing what /// prevents the signal from being sent. //------------------------------------------------------------------ virtual Status WillSignal() { return Status(); } //------------------------------------------------------------------ /// Sends a process a UNIX signal \a signal. /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Status DoSignal(int signal) { Status error; error.SetErrorStringWithFormat( "error: %s does not support sending signals to processes", GetPluginName().GetCString()); return error; } virtual Status WillDestroy() { return Status(); } virtual Status DoDestroy() = 0; virtual void DidDestroy() {} virtual bool DestroyRequiresHalt() { return true; } //------------------------------------------------------------------ /// Called after sending a signal to a process. /// /// Allow Process plug-ins to execute some code after sending a /// signal to a process. //------------------------------------------------------------------ virtual void DidSignal() {} //------------------------------------------------------------------ /// Currently called as part of ShouldStop. /// FIXME: Should really happen when the target stops before the /// event is taken from the queue... /// /// This callback is called as the event /// is about to be queued up to allow Process plug-ins to execute /// some code prior to clients being notified that a process was /// stopped. Common operations include updating the thread list, /// invalidating any thread state (registers, stack, etc) prior to /// letting the notification go out. /// //------------------------------------------------------------------ virtual void RefreshStateAfterStop() = 0; //------------------------------------------------------------------ /// Sometimes the connection to a process can detect the host OS /// version that the process is running on. The current platform /// should be checked first in case the platform is connected, but /// clients can fall back onto this function if the platform fails /// to identify the host OS version. The platform should be checked /// first in case you are running a simulator platform that might /// itself be running natively, but have different heuristics for /// figuring out which OS is is emulating. /// /// @param[out] major /// The major OS version, or UINT32_MAX if it can't be determined /// /// @param[out] minor /// The minor OS version, or UINT32_MAX if it can't be determined /// /// @param[out] update /// The update OS version, or UINT32_MAX if it can't be determined /// /// @return /// Returns \b true if the host OS version info was filled in /// and \b false otherwise. //------------------------------------------------------------------ virtual bool GetHostOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) { major = UINT32_MAX; minor = UINT32_MAX; update = UINT32_MAX; return false; } //------------------------------------------------------------------ /// Get the target object pointer for this module. /// /// @return /// A Target object pointer to the target that owns this /// module. //------------------------------------------------------------------ Target &GetTarget() { return *m_target_sp.lock(); } //------------------------------------------------------------------ /// Get the const target object pointer for this module. /// /// @return /// A const Target object pointer to the target that owns this /// module. //------------------------------------------------------------------ const Target &GetTarget() const { return *m_target_sp.lock(); } //------------------------------------------------------------------ /// Flush all data in the process. /// /// Flush the memory caches, all threads, and any other cached data /// in the process. /// /// This function can be called after a world changing event like /// adding a new symbol file, or after the process makes a large /// context switch (from boot ROM to booted into an OS). //------------------------------------------------------------------ void Flush(); //------------------------------------------------------------------ /// Get accessor for the current process state. /// /// @return /// The current state of the process. /// /// @see lldb::StateType //------------------------------------------------------------------ lldb::StateType GetState(); lldb::ExpressionResults RunThreadPlan(ExecutionContext &exe_ctx, lldb::ThreadPlanSP &thread_plan_sp, const EvaluateExpressionOptions &options, DiagnosticManager &diagnostic_manager); static const char *ExecutionResultAsCString(lldb::ExpressionResults result); void GetStatus(Stream &ostrm); size_t GetThreadStatus(Stream &ostrm, bool only_threads_with_stop_reason, uint32_t start_frame, uint32_t num_frames, uint32_t num_frames_with_source, bool stop_format); void SendAsyncInterrupt(); //------------------------------------------------------------------ // Notify this process class that modules got loaded. // // If subclasses override this method, they must call this version // before doing anything in the subclass version of the function. //------------------------------------------------------------------ virtual void ModulesDidLoad(ModuleList &module_list); //------------------------------------------------------------------ /// Retrieve the list of shared libraries that are loaded for this process /// This method is used on pre-macOS 10.12, pre-iOS 10, pre-tvOS 10, /// pre-watchOS 3 systems. The following two methods are for newer versions /// of those OSes. /// /// For certain platforms, the time it takes for the DynamicLoader plugin to /// read all of the shared libraries out of memory over a slow communication /// channel may be too long. In that instance, the gdb-remote stub may be /// able to retrieve the necessary information about the solibs out of memory /// and return a concise summary sufficient for the DynamicLoader plugin. /// /// @param [in] image_list_address /// The address where the table of shared libraries is stored in memory, /// if that is appropriate for this platform. Else this may be /// passed as LLDB_INVALID_ADDRESS. /// /// @param [in] image_count /// The number of shared libraries that are present in this process, if /// that is appropriate for this platofrm Else this may be passed as /// LLDB_INVALID_ADDRESS. /// /// @return /// A StructureDataSP object which, if non-empty, will contain the /// information the DynamicLoader needs to get the initial scan of /// solibs resolved. //------------------------------------------------------------------ virtual lldb_private::StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos(lldb::addr_t image_list_address, lldb::addr_t image_count) { return StructuredData::ObjectSP(); } // On macOS 10.12, tvOS 10, iOS 10, watchOS 3 and newer, debugserver can // return // the full list of loaded shared libraries without needing any input. virtual lldb_private::StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos() { return StructuredData::ObjectSP(); } // On macOS 10.12, tvOS 10, iOS 10, watchOS 3 and newer, debugserver can // return // information about binaries given their load addresses. virtual lldb_private::StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos( const std::vector &load_addresses) { return StructuredData::ObjectSP(); } //------------------------------------------------------------------ // Get information about the library shared cache, if that exists // // On macOS 10.12, tvOS 10, iOS 10, watchOS 3 and newer, debugserver can // return // information about the library shared cache (a set of standard libraries // that are // loaded at the same location for all processes on a system) in use. //------------------------------------------------------------------ virtual lldb_private::StructuredData::ObjectSP GetSharedCacheInfo() { return StructuredData::ObjectSP(); } //------------------------------------------------------------------ /// Print a user-visible warning about a module being built with optimization /// /// Prints a async warning message to the user one time per Module /// where a function is found that was compiled with optimization, per /// Process. /// /// @param [in] sc /// A SymbolContext with eSymbolContextFunction and eSymbolContextModule /// pre-computed. //------------------------------------------------------------------ void PrintWarningOptimization(const SymbolContext &sc); virtual bool GetProcessInfo(ProcessInstanceInfo &info); public: //------------------------------------------------------------------ /// Get the exit status for a process. /// /// @return /// The process's return code, or -1 if the current process /// state is not eStateExited. //------------------------------------------------------------------ int GetExitStatus(); //------------------------------------------------------------------ /// Get a textual description of what the process exited. /// /// @return /// The textual description of why the process exited, or nullptr /// if there is no description available. //------------------------------------------------------------------ const char *GetExitDescription(); virtual void DidExit() {} //------------------------------------------------------------------ /// Get the Modification ID of the process. /// /// @return /// The modification ID of the process. //------------------------------------------------------------------ ProcessModID GetModID() const { return m_mod_id; } const ProcessModID &GetModIDRef() const { return m_mod_id; } uint32_t GetStopID() const { return m_mod_id.GetStopID(); } uint32_t GetResumeID() const { return m_mod_id.GetResumeID(); } uint32_t GetLastUserExpressionResumeID() const { return m_mod_id.GetLastUserExpressionResumeID(); } uint32_t GetLastNaturalStopID() const { return m_mod_id.GetLastNaturalStopID(); } lldb::EventSP GetStopEventForStopID(uint32_t stop_id) const { return m_mod_id.GetStopEventForStopID(stop_id); } //------------------------------------------------------------------ /// Set accessor for the process exit status (return code). /// /// Sometimes a child exits and the exit can be detected by global /// functions (signal handler for SIGCHLD for example). This /// accessor allows the exit status to be set from an external /// source. /// /// Setting this will cause a eStateExited event to be posted to /// the process event queue. /// /// @param[in] exit_status /// The value for the process's return code. /// /// @see lldb::StateType //------------------------------------------------------------------ virtual bool SetExitStatus(int exit_status, const char *cstr); //------------------------------------------------------------------ /// Check if a process is still alive. /// /// @return /// Returns \b true if the process is still valid, \b false /// otherwise. //------------------------------------------------------------------ virtual bool IsAlive(); //------------------------------------------------------------------ /// Before lldb detaches from a process, it warns the user that they are about /// to lose their debug session. /// In some cases, this warning doesn't need to be emitted -- for instance, /// with core file debugging where /// the user can reconstruct the "state" by simply re-running the debugger on /// the core file. /// /// @return // true if the user should be warned about detaching from this process. //------------------------------------------------------------------ virtual bool WarnBeforeDetach() const { return true; } //------------------------------------------------------------------ /// Actually do the reading of memory from a process. /// /// Subclasses must override this function and can return fewer /// bytes than requested when memory requests are too large. This /// class will break up the memory requests and keep advancing the /// arguments along as needed. /// /// @param[in] vm_addr /// A virtual load address that indicates where to start reading /// memory from. /// /// @param[in] size /// The number of bytes to read. /// /// @param[out] buf /// A byte buffer that is at least \a size bytes long that /// will receive the memory bytes. /// /// @return /// The number of bytes that were actually read into \a buf. //------------------------------------------------------------------ virtual size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error) = 0; //------------------------------------------------------------------ /// Read of memory from a process. /// /// This function will read memory from the current process's /// address space and remove any traps that may have been inserted /// into the memory. /// /// This function is not meant to be overridden by Process /// subclasses, the subclasses should implement /// Process::DoReadMemory (lldb::addr_t, size_t, void *). /// /// @param[in] vm_addr /// A virtual load address that indicates where to start reading /// memory from. /// /// @param[out] buf /// A byte buffer that is at least \a size bytes long that /// will receive the memory bytes. /// /// @param[in] size /// The number of bytes to read. /// /// @return /// The number of bytes that were actually read into \a buf. If /// the returned number is greater than zero, yet less than \a /// size, then this function will get called again with \a /// vm_addr, \a buf, and \a size updated appropriately. Zero is /// returned to indicate an error. //------------------------------------------------------------------ virtual size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error); //------------------------------------------------------------------ /// Read a NULL terminated string from memory /// /// This function will read a cache page at a time until a NULL /// string terminator is found. It will stop reading if an aligned /// sequence of NULL termination \a type_width bytes is not found /// before reading \a cstr_max_len bytes. The results are always /// guaranteed to be NULL terminated, and that no more than /// (max_bytes - type_width) bytes will be read. /// /// @param[in] vm_addr /// The virtual load address to start the memory read. /// /// @param[in] str /// A character buffer containing at least max_bytes. /// /// @param[in] max_bytes /// The maximum number of bytes to read. /// /// @param[in] error /// The error status of the read operation. /// /// @param[in] type_width /// The size of the null terminator (1 to 4 bytes per /// character). Defaults to 1. /// /// @return /// The error status or the number of bytes prior to the null terminator. //------------------------------------------------------------------ size_t ReadStringFromMemory(lldb::addr_t vm_addr, char *str, size_t max_bytes, Status &error, size_t type_width = 1); //------------------------------------------------------------------ /// Read a NULL terminated C string from memory /// /// This function will read a cache page at a time until the NULL /// C string terminator is found. It will stop reading if the NULL /// termination byte isn't found before reading \a cstr_max_len /// bytes, and the results are always guaranteed to be NULL /// terminated (at most cstr_max_len - 1 bytes will be read). //------------------------------------------------------------------ size_t ReadCStringFromMemory(lldb::addr_t vm_addr, char *cstr, size_t cstr_max_len, Status &error); size_t ReadCStringFromMemory(lldb::addr_t vm_addr, std::string &out_str, Status &error); size_t ReadMemoryFromInferior(lldb::addr_t vm_addr, void *buf, size_t size, Status &error); //------------------------------------------------------------------ /// Reads an unsigned integer of the specified byte size from /// process memory. /// /// @param[in] load_addr /// A load address of the integer to read. /// /// @param[in] byte_size /// The size in byte of the integer to read. /// /// @param[in] fail_value /// The value to return if we fail to read an integer. /// /// @param[out] error /// An error that indicates the success or failure of this /// operation. If error indicates success (error.Success()), /// then the value returned can be trusted, otherwise zero /// will be returned. /// /// @return /// The unsigned integer that was read from the process memory /// space. If the integer was smaller than a uint64_t, any /// unused upper bytes will be zero filled. If the process /// byte order differs from the host byte order, the integer /// value will be appropriately byte swapped into host byte /// order. //------------------------------------------------------------------ uint64_t ReadUnsignedIntegerFromMemory(lldb::addr_t load_addr, size_t byte_size, uint64_t fail_value, Status &error); int64_t ReadSignedIntegerFromMemory(lldb::addr_t load_addr, size_t byte_size, int64_t fail_value, Status &error); lldb::addr_t ReadPointerFromMemory(lldb::addr_t vm_addr, Status &error); bool WritePointerToMemory(lldb::addr_t vm_addr, lldb::addr_t ptr_value, Status &error); //------------------------------------------------------------------ /// Actually do the writing of memory to a process. /// /// @param[in] vm_addr /// A virtual load address that indicates where to start writing /// memory to. /// /// @param[in] buf /// A byte buffer that is at least \a size bytes long that /// contains the data to write. /// /// @param[in] size /// The number of bytes to write. /// /// @param[out] error /// An error value in case the memory write fails. /// /// @return /// The number of bytes that were actually written. //------------------------------------------------------------------ virtual size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, Status &error) { error.SetErrorStringWithFormat( "error: %s does not support writing to processes", GetPluginName().GetCString()); return 0; } //------------------------------------------------------------------ /// Write all or part of a scalar value to memory. /// /// The value contained in \a scalar will be swapped to match the /// byte order of the process that is being debugged. If \a size is /// less than the size of scalar, the least significant \a size bytes /// from scalar will be written. If \a size is larger than the byte /// size of scalar, then the extra space will be padded with zeros /// and the scalar value will be placed in the least significant /// bytes in memory. /// /// @param[in] vm_addr /// A virtual load address that indicates where to start writing /// memory to. /// /// @param[in] scalar /// The scalar to write to the debugged process. /// /// @param[in] size /// This value can be smaller or larger than the scalar value /// itself. If \a size is smaller than the size of \a scalar, /// the least significant bytes in \a scalar will be used. If /// \a size is larger than the byte size of \a scalar, then /// the extra space will be padded with zeros. If \a size is /// set to UINT32_MAX, then the size of \a scalar will be used. /// /// @param[out] error /// An error value in case the memory write fails. /// /// @return /// The number of bytes that were actually written. //------------------------------------------------------------------ size_t WriteScalarToMemory(lldb::addr_t vm_addr, const Scalar &scalar, size_t size, Status &error); size_t ReadScalarIntegerFromMemory(lldb::addr_t addr, uint32_t byte_size, bool is_signed, Scalar &scalar, Status &error); //------------------------------------------------------------------ /// Write memory to a process. /// /// This function will write memory to the current process's /// address space and maintain any traps that might be present due /// to software breakpoints. /// /// This function is not meant to be overridden by Process /// subclasses, the subclasses should implement /// Process::DoWriteMemory (lldb::addr_t, size_t, void *). /// /// @param[in] vm_addr /// A virtual load address that indicates where to start writing /// memory to. /// /// @param[in] buf /// A byte buffer that is at least \a size bytes long that /// contains the data to write. /// /// @param[in] size /// The number of bytes to write. /// /// @return /// The number of bytes that were actually written. //------------------------------------------------------------------ // TODO: change this to take an ArrayRef size_t WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, Status &error); //------------------------------------------------------------------ /// Actually allocate memory in the process. /// /// This function will allocate memory in the process's address /// space. This can't rely on the generic function calling mechanism, /// since that requires this function. /// /// @param[in] size /// The size of the allocation requested. /// /// @return /// The address of the allocated buffer in the process, or /// LLDB_INVALID_ADDRESS if the allocation failed. //------------------------------------------------------------------ virtual lldb::addr_t DoAllocateMemory(size_t size, uint32_t permissions, Status &error) { error.SetErrorStringWithFormat( "error: %s does not support allocating in the debug process", GetPluginName().GetCString()); return LLDB_INVALID_ADDRESS; } //------------------------------------------------------------------ /// The public interface to allocating memory in the process. /// /// This function will allocate memory in the process's address /// space. This can't rely on the generic function calling mechanism, /// since that requires this function. /// /// @param[in] size /// The size of the allocation requested. /// /// @param[in] permissions /// Or together any of the lldb::Permissions bits. The permissions on /// a given memory allocation can't be changed after allocation. Note /// that a block that isn't set writable can still be written on from /// lldb, /// just not by the process itself. /// /// @param[in,out] error /// An error object to fill in if things go wrong. /// @return /// The address of the allocated buffer in the process, or /// LLDB_INVALID_ADDRESS if the allocation failed. //------------------------------------------------------------------ lldb::addr_t AllocateMemory(size_t size, uint32_t permissions, Status &error); //------------------------------------------------------------------ /// The public interface to allocating memory in the process, this also /// clears the allocated memory. /// /// This function will allocate memory in the process's address /// space. This can't rely on the generic function calling mechanism, /// since that requires this function. /// /// @param[in] size /// The size of the allocation requested. /// /// @param[in] permissions /// Or together any of the lldb::Permissions bits. The permissions on /// a given memory allocation can't be changed after allocation. Note /// that a block that isn't set writable can still be written on from /// lldb, /// just not by the process itself. /// /// @param[in/out] error /// An error object to fill in if things go wrong. /// @return /// The address of the allocated buffer in the process, or /// LLDB_INVALID_ADDRESS if the allocation failed. //------------------------------------------------------------------ lldb::addr_t CallocateMemory(size_t size, uint32_t permissions, Status &error); //------------------------------------------------------------------ /// Resolve dynamically loaded indirect functions. /// /// @param[in] address /// The load address of the indirect function to resolve. /// /// @param[out] error /// An error value in case the resolve fails. /// /// @return /// The address of the resolved function. /// LLDB_INVALID_ADDRESS if the resolution failed. //------------------------------------------------------------------ virtual lldb::addr_t ResolveIndirectFunction(const Address *address, Status &error); //------------------------------------------------------------------ /// Locate the memory region that contains load_addr. /// /// If load_addr is within the address space the process has mapped /// range_info will be filled in with the start and end of that range /// as well as the permissions for that range and range_info.GetMapped /// will return true. /// /// If load_addr is outside any mapped region then range_info will /// have its start address set to load_addr and the end of the /// range will indicate the start of the next mapped range or be /// set to LLDB_INVALID_ADDRESS if there are no valid mapped ranges /// between load_addr and the end of the process address space. /// /// GetMemoryRegionInfo will only return an error if it is /// unimplemented for the current process. /// /// @param[in] load_addr /// The load address to query the range_info for. /// /// @param[out] range_info /// An range_info value containing the details of the range. /// /// @return /// An error value. //------------------------------------------------------------------ virtual Status GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info) { Status error; error.SetErrorString("Process::GetMemoryRegionInfo() not supported"); return error; } //------------------------------------------------------------------ /// Obtain all the mapped memory regions within this process. /// /// @param[out] region_list /// A vector to contain MemoryRegionInfo objects for all mapped /// ranges. /// /// @return /// An error value. //------------------------------------------------------------------ virtual Status GetMemoryRegions(std::vector ®ion_list); virtual Status GetWatchpointSupportInfo(uint32_t &num) { Status error; num = 0; error.SetErrorString("Process::GetWatchpointSupportInfo() not supported"); return error; } virtual Status GetWatchpointSupportInfo(uint32_t &num, bool &after) { Status error; num = 0; after = true; error.SetErrorString("Process::GetWatchpointSupportInfo() not supported"); return error; } lldb::ModuleSP ReadModuleFromMemory(const FileSpec &file_spec, lldb::addr_t header_addr, size_t size_to_read = 512); //------------------------------------------------------------------ /// Attempt to get the attributes for a region of memory in the process. /// /// It may be possible for the remote debug server to inspect attributes /// for a region of memory in the process, such as whether there is a /// valid page of memory at a given address or whether that page is /// readable/writable/executable by the process. /// /// @param[in] load_addr /// The address of interest in the process. /// /// @param[out] permissions /// If this call returns successfully, this bitmask will have /// its Permissions bits set to indicate whether the region is /// readable/writable/executable. If this call fails, the /// bitmask values are undefined. /// /// @return /// Returns true if it was able to determine the attributes of the /// memory region. False if not. //------------------------------------------------------------------ virtual bool GetLoadAddressPermissions(lldb::addr_t load_addr, uint32_t &permissions); //------------------------------------------------------------------ /// Determines whether executing JIT-compiled code in this process /// is possible. /// /// @return /// True if execution of JIT code is possible; false otherwise. //------------------------------------------------------------------ bool CanJIT(); //------------------------------------------------------------------ /// Sets whether executing JIT-compiled code in this process /// is possible. /// /// @param[in] can_jit /// True if execution of JIT code is possible; false otherwise. //------------------------------------------------------------------ void SetCanJIT(bool can_jit); //------------------------------------------------------------------ /// Determines whether executing function calls using the interpreter /// is possible for this process. /// /// @return /// True if possible; false otherwise. //------------------------------------------------------------------ bool CanInterpretFunctionCalls() { return m_can_interpret_function_calls; } //------------------------------------------------------------------ /// Sets whether executing function calls using the interpreter /// is possible for this process. /// /// @param[in] can_interpret_function_calls /// True if possible; false otherwise. //------------------------------------------------------------------ void SetCanInterpretFunctionCalls(bool can_interpret_function_calls) { m_can_interpret_function_calls = can_interpret_function_calls; } //------------------------------------------------------------------ /// Sets whether executing code in this process is possible. /// This could be either through JIT or interpreting. /// /// @param[in] can_run_code /// True if execution of code is possible; false otherwise. //------------------------------------------------------------------ void SetCanRunCode(bool can_run_code); //------------------------------------------------------------------ /// Actually deallocate memory in the process. /// /// This function will deallocate memory in the process's address /// space that was allocated with AllocateMemory. /// /// @param[in] ptr /// A return value from AllocateMemory, pointing to the memory you /// want to deallocate. /// /// @return /// \btrue if the memory was deallocated, \bfalse otherwise. //------------------------------------------------------------------ virtual Status DoDeallocateMemory(lldb::addr_t ptr) { Status error; error.SetErrorStringWithFormat( "error: %s does not support deallocating in the debug process", GetPluginName().GetCString()); return error; } //------------------------------------------------------------------ /// The public interface to deallocating memory in the process. /// /// This function will deallocate memory in the process's address /// space that was allocated with AllocateMemory. /// /// @param[in] ptr /// A return value from AllocateMemory, pointing to the memory you /// want to deallocate. /// /// @return /// \btrue if the memory was deallocated, \bfalse otherwise. //------------------------------------------------------------------ Status DeallocateMemory(lldb::addr_t ptr); //------------------------------------------------------------------ /// Get any available STDOUT. /// /// Calling this method is a valid operation only if all of the /// following conditions are true: /// 1) The process was launched, and not attached to. /// 2) The process was not launched with eLaunchFlagDisableSTDIO. /// 3) The process was launched without supplying a valid file path /// for STDOUT. /// /// Note that the implementation will probably need to start a read /// thread in the background to make sure that the pipe is drained /// and the STDOUT buffered appropriately, to prevent the process /// from deadlocking trying to write to a full buffer. /// /// Events will be queued indicating that there is STDOUT available /// that can be retrieved using this function. /// /// @param[out] buf /// A buffer that will receive any STDOUT bytes that are /// currently available. /// /// @param[in] buf_size /// The size in bytes for the buffer \a buf. /// /// @return /// The number of bytes written into \a buf. If this value is /// equal to \a buf_size, another call to this function should /// be made to retrieve more STDOUT data. //------------------------------------------------------------------ virtual size_t GetSTDOUT(char *buf, size_t buf_size, Status &error); //------------------------------------------------------------------ /// Get any available STDERR. /// /// Calling this method is a valid operation only if all of the /// following conditions are true: /// 1) The process was launched, and not attached to. /// 2) The process was not launched with eLaunchFlagDisableSTDIO. /// 3) The process was launched without supplying a valid file path /// for STDERR. /// /// Note that the implementation will probably need to start a read /// thread in the background to make sure that the pipe is drained /// and the STDERR buffered appropriately, to prevent the process /// from deadlocking trying to write to a full buffer. /// /// Events will be queued indicating that there is STDERR available /// that can be retrieved using this function. /// /// @param[in] buf /// A buffer that will receive any STDERR bytes that are /// currently available. /// /// @param[out] buf_size /// The size in bytes for the buffer \a buf. /// /// @return /// The number of bytes written into \a buf. If this value is /// equal to \a buf_size, another call to this function should /// be made to retrieve more STDERR data. //------------------------------------------------------------------ virtual size_t GetSTDERR(char *buf, size_t buf_size, Status &error); //------------------------------------------------------------------ /// Puts data into this process's STDIN. /// /// Calling this method is a valid operation only if all of the /// following conditions are true: /// 1) The process was launched, and not attached to. /// 2) The process was not launched with eLaunchFlagDisableSTDIO. /// 3) The process was launched without supplying a valid file path /// for STDIN. /// /// @param[in] buf /// A buffer that contains the data to write to the process's STDIN. /// /// @param[in] buf_size /// The size in bytes for the buffer \a buf. /// /// @return /// The number of bytes written into \a buf. If this value is /// less than \a buf_size, another call to this function should /// be made to write the rest of the data. //------------------------------------------------------------------ virtual size_t PutSTDIN(const char *buf, size_t buf_size, Status &error) { error.SetErrorString("stdin unsupported"); return 0; } //------------------------------------------------------------------ /// Get any available profile data. /// /// @param[out] buf /// A buffer that will receive any profile data bytes that are /// currently available. /// /// @param[out] buf_size /// The size in bytes for the buffer \a buf. /// /// @return /// The number of bytes written into \a buf. If this value is /// equal to \a buf_size, another call to this function should /// be made to retrieve more profile data. //------------------------------------------------------------------ virtual size_t GetAsyncProfileData(char *buf, size_t buf_size, Status &error); //---------------------------------------------------------------------- // Process Breakpoints //---------------------------------------------------------------------- size_t GetSoftwareBreakpointTrapOpcode(BreakpointSite *bp_site); virtual Status EnableBreakpointSite(BreakpointSite *bp_site) { Status error; error.SetErrorStringWithFormat( "error: %s does not support enabling breakpoints", GetPluginName().GetCString()); return error; } virtual Status DisableBreakpointSite(BreakpointSite *bp_site) { Status error; error.SetErrorStringWithFormat( "error: %s does not support disabling breakpoints", GetPluginName().GetCString()); return error; } // This is implemented completely using the lldb::Process API. Subclasses // don't need to implement this function unless the standard flow of // read existing opcode, write breakpoint opcode, verify breakpoint opcode // doesn't work for a specific process plug-in. virtual Status EnableSoftwareBreakpoint(BreakpointSite *bp_site); // This is implemented completely using the lldb::Process API. Subclasses // don't need to implement this function unless the standard flow of // restoring original opcode in memory and verifying the restored opcode // doesn't work for a specific process plug-in. virtual Status DisableSoftwareBreakpoint(BreakpointSite *bp_site); BreakpointSiteList &GetBreakpointSiteList(); const BreakpointSiteList &GetBreakpointSiteList() const; void DisableAllBreakpointSites(); Status ClearBreakpointSiteByID(lldb::user_id_t break_id); lldb::break_id_t CreateBreakpointSite(const lldb::BreakpointLocationSP &owner, bool use_hardware); Status DisableBreakpointSiteByID(lldb::user_id_t break_id); Status EnableBreakpointSiteByID(lldb::user_id_t break_id); // BreakpointLocations use RemoveOwnerFromBreakpointSite to remove // themselves from the owner's list of this breakpoint sites. void RemoveOwnerFromBreakpointSite(lldb::user_id_t owner_id, lldb::user_id_t owner_loc_id, lldb::BreakpointSiteSP &bp_site_sp); //---------------------------------------------------------------------- // Process Watchpoints (optional) //---------------------------------------------------------------------- virtual Status EnableWatchpoint(Watchpoint *wp, bool notify = true); virtual Status DisableWatchpoint(Watchpoint *wp, bool notify = true); //------------------------------------------------------------------ // Thread Queries //------------------------------------------------------------------ virtual bool UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) = 0; void UpdateThreadListIfNeeded(); ThreadList &GetThreadList() { return m_thread_list; } // When ExtendedBacktraces are requested, the HistoryThreads that are // created need an owner -- they're saved here in the Process. The // threads in this list are not iterated over - driver programs need to // request the extended backtrace calls starting from a root concrete // thread one by one. ThreadList &GetExtendedThreadList() { return m_extended_thread_list; } ThreadList::ThreadIterable Threads() { return m_thread_list.Threads(); } uint32_t GetNextThreadIndexID(uint64_t thread_id); lldb::ThreadSP CreateOSPluginThread(lldb::tid_t tid, lldb::addr_t context); // Returns true if an index id has been assigned to a thread. bool HasAssignedIndexIDToThread(uint64_t sb_thread_id); // Given a thread_id, it will assign a more reasonable index id for display to // the user. // If the thread_id has previously been assigned, the same index id will be // used. uint32_t AssignIndexIDToThread(uint64_t thread_id); //------------------------------------------------------------------ // Queue Queries //------------------------------------------------------------------ void UpdateQueueListIfNeeded(); QueueList &GetQueueList() { UpdateQueueListIfNeeded(); return m_queue_list; } QueueList::QueueIterable Queues() { UpdateQueueListIfNeeded(); return m_queue_list.Queues(); } //------------------------------------------------------------------ // Event Handling //------------------------------------------------------------------ lldb::StateType GetNextEvent(lldb::EventSP &event_sp); // Returns the process state when it is stopped. If specified, event_sp_ptr // is set to the event which triggered the stop. If wait_always = false, // and the process is already stopped, this function returns immediately. // If the process is hijacked and use_run_lock is true (the default), then // this // function releases the run lock after the stop. Setting use_run_lock to // false // will avoid this behavior. lldb::StateType WaitForProcessToStop(const Timeout &timeout, lldb::EventSP *event_sp_ptr = nullptr, bool wait_always = true, lldb::ListenerSP hijack_listener = lldb::ListenerSP(), Stream *stream = nullptr, bool use_run_lock = true); uint32_t GetIOHandlerID() const { return m_iohandler_sync.GetValue(); } //-------------------------------------------------------------------------------------- /// Waits for the process state to be running within a given msec timeout. /// /// The main purpose of this is to implement an interlock waiting for /// HandlePrivateEvent /// to push an IOHandler. /// /// @param[in] timeout_msec /// The maximum time length to wait for the process to transition to the /// eStateRunning state, specified in milliseconds. //-------------------------------------------------------------------------------------- void SyncIOHandler(uint32_t iohandler_id, uint64_t timeout_msec); lldb::StateType GetStateChangedEvents( lldb::EventSP &event_sp, const Timeout &timeout, lldb::ListenerSP hijack_listener); // Pass an empty ListenerSP to use builtin listener //-------------------------------------------------------------------------------------- /// Centralize the code that handles and prints descriptions for process state /// changes. /// /// @param[in] event_sp /// The process state changed event /// /// @param[in] stream /// The output stream to get the state change description /// /// @param[in,out] pop_process_io_handler /// If this value comes in set to \b true, then pop the Process IOHandler /// if needed. /// Else this variable will be set to \b true or \b false to indicate if /// the process /// needs to have its process IOHandler popped. /// /// @return /// \b true if the event describes a process state changed event, \b false /// otherwise. //-------------------------------------------------------------------------------------- static bool HandleProcessStateChangedEvent(const lldb::EventSP &event_sp, Stream *stream, bool &pop_process_io_handler); Event *PeekAtStateChangedEvents(); class ProcessEventHijacker { public: ProcessEventHijacker(Process &process, lldb::ListenerSP listener_sp) : m_process(process) { m_process.HijackProcessEvents(listener_sp); } ~ProcessEventHijacker() { m_process.RestoreProcessEvents(); } private: Process &m_process; }; friend class ProcessEventHijacker; friend class ProcessProperties; //------------------------------------------------------------------ /// If you need to ensure that you and only you will hear about some public /// event, then make a new listener, set to listen to process events, and /// then call this with that listener. Then you will have to wait on that /// listener explicitly for events (rather than using the GetNextEvent & /// WaitFor* /// calls above. Be sure to call RestoreProcessEvents when you are done. /// /// @param[in] listener /// This is the new listener to whom all process events will be delivered. /// /// @return /// Returns \b true if the new listener could be installed, /// \b false otherwise. //------------------------------------------------------------------ bool HijackProcessEvents(lldb::ListenerSP listener_sp); //------------------------------------------------------------------ /// Restores the process event broadcasting to its normal state. /// //------------------------------------------------------------------ void RestoreProcessEvents(); const lldb::ABISP &GetABI(); OperatingSystem *GetOperatingSystem() { return m_os_ap.get(); } ArchSpec::StopInfoOverrideCallbackType GetStopInfoOverrideCallback() const { return m_stop_info_override_callback; } virtual LanguageRuntime *GetLanguageRuntime(lldb::LanguageType language, bool retry_if_null = true); virtual CPPLanguageRuntime *GetCPPLanguageRuntime(bool retry_if_null = true); virtual ObjCLanguageRuntime * GetObjCLanguageRuntime(bool retry_if_null = true); bool IsPossibleDynamicValue(ValueObject &in_value); bool IsRunning() const; DynamicCheckerFunctions *GetDynamicCheckers() { return m_dynamic_checkers_ap.get(); } void SetDynamicCheckers(DynamicCheckerFunctions *dynamic_checkers); //------------------------------------------------------------------ /// Call this to set the lldb in the mode where it breaks on new thread /// creations, and then auto-restarts. This is useful when you are trying /// to run only one thread, but either that thread or the kernel is creating /// new threads in the process. If you stop when the thread is created, you /// can immediately suspend it, and keep executing only the one thread you /// intend. /// /// @return /// Returns \b true if we were able to start up the notification /// \b false otherwise. //------------------------------------------------------------------ virtual bool StartNoticingNewThreads() { return true; } //------------------------------------------------------------------ /// Call this to turn off the stop & notice new threads mode. /// /// @return /// Returns \b true if we were able to start up the notification /// \b false otherwise. //------------------------------------------------------------------ virtual bool StopNoticingNewThreads() { return true; } void SetRunningUserExpression(bool on); //------------------------------------------------------------------ // lldb::ExecutionContextScope pure virtual functions //------------------------------------------------------------------ lldb::TargetSP CalculateTarget() override; lldb::ProcessSP CalculateProcess() override { return shared_from_this(); } lldb::ThreadSP CalculateThread() override { return lldb::ThreadSP(); } lldb::StackFrameSP CalculateStackFrame() override { return lldb::StackFrameSP(); } void CalculateExecutionContext(ExecutionContext &exe_ctx) override; void SetSTDIOFileDescriptor(int file_descriptor); //------------------------------------------------------------------ // Add a permanent region of memory that should never be read or // written to. This can be used to ensure that memory reads or writes // to certain areas of memory never end up being sent to the // DoReadMemory or DoWriteMemory functions which can improve // performance. //------------------------------------------------------------------ void AddInvalidMemoryRegion(const LoadRange ®ion); //------------------------------------------------------------------ // Remove a permanent region of memory that should never be read or // written to that was previously added with AddInvalidMemoryRegion. //------------------------------------------------------------------ bool RemoveInvalidMemoryRange(const LoadRange ®ion); //------------------------------------------------------------------ // If the setup code of a thread plan needs to do work that might involve // calling a function in the target, it should not do that work directly // in one of the thread plan functions (DidPush/WillResume) because // such work needs to be handled carefully. Instead, put that work in // a PreResumeAction callback, and register it with the process. It will // get done before the actual "DoResume" gets called. //------------------------------------------------------------------ typedef bool(PreResumeActionCallback)(void *); void AddPreResumeAction(PreResumeActionCallback callback, void *baton); bool RunPreResumeActions(); void ClearPreResumeActions(); void ClearPreResumeAction(PreResumeActionCallback callback, void *baton); ProcessRunLock &GetRunLock(); virtual Status SendEventData(const char *data) { Status return_error("Sending an event is not supported for this process."); return return_error; } lldb::ThreadCollectionSP GetHistoryThreads(lldb::addr_t addr); lldb::InstrumentationRuntimeSP GetInstrumentationRuntime(lldb::InstrumentationRuntimeType type); //------------------------------------------------------------------ /// Try to fetch the module specification for a module with the /// given file name and architecture. Process sub-classes have to /// override this method if they support platforms where the /// Platform object can't get the module spec for all module. /// /// @param[in] module_file_spec /// The file name of the module to get specification for. /// /// @param[in] arch /// The architecture of the module to get specification for. /// /// @param[out] module_spec /// The fetched module specification if the return value is /// \b true, unchanged otherwise. /// /// @return /// Returns \b true if the module spec fetched successfully, /// \b false otherwise. //------------------------------------------------------------------ virtual bool GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, ModuleSpec &module_spec); virtual void PrefetchModuleSpecs(llvm::ArrayRef module_file_specs, const llvm::Triple &triple) {} //------------------------------------------------------------------ /// Try to find the load address of a file. /// The load address is defined as the address of the first memory /// region what contains data mapped from the specified file. /// /// @param[in] file /// The name of the file whose load address we are looking for /// /// @param[out] is_loaded /// \b True if the file is loaded into the memory and false /// otherwise. /// /// @param[out] load_addr /// The load address of the file if it is loaded into the /// processes address space, LLDB_INVALID_ADDRESS otherwise. //------------------------------------------------------------------ virtual Status GetFileLoadAddress(const FileSpec &file, bool &is_loaded, lldb::addr_t &load_addr) { return Status("Not supported"); } size_t AddImageToken(lldb::addr_t image_ptr); lldb::addr_t GetImagePtrFromToken(size_t token) const; void ResetImageToken(size_t token); //------------------------------------------------------------------ /// Find the next branch instruction to set a breakpoint on /// /// When instruction stepping through a source line, instead of /// stepping through each instruction, we can put a breakpoint on /// the next branch instruction (within the range of instructions /// we are stepping through) and continue the process to there, /// yielding significant performance benefits over instruction /// stepping. /// /// @param[in] default_stop_addr /// The address of the instruction where lldb would put a /// breakpoint normally. /// /// @param[in] range_bounds /// The range which the breakpoint must be contained within. /// Typically a source line. /// /// @return /// The address of the next branch instruction, or the end of /// the range provided in range_bounds. If there are any /// problems with the disassembly or getting the instructions, /// the original default_stop_addr will be returned. //------------------------------------------------------------------ Address AdvanceAddressToNextBranchInstruction(Address default_stop_addr, AddressRange range_bounds); //------------------------------------------------------------------ /// Configure asynchronous structured data feature. /// /// Each Process type that supports using an asynchronous StructuredData /// feature should implement this to enable/disable/configure the feature. /// The default implementation here will always return an error indiciating /// the feature is unsupported. /// /// StructuredDataPlugin implementations will call this to configure /// a feature that has been reported as being supported. /// /// @param[in] type_name /// The StructuredData type name as previously discovered by /// the Process-derived instance. /// /// @param[in] config /// Configuration data for the feature being enabled. This config /// data, which may be null, will be passed along to the feature /// to process. The feature will dictate whether this is a dictionary, /// an array or some other object. If the feature needs to be /// set up properly before it can be enabled, then the config should /// also take an enable/disable flag. /// /// @return /// Returns the result of attempting to configure the feature. //------------------------------------------------------------------ virtual Status ConfigureStructuredData(const ConstString &type_name, const StructuredData::ObjectSP &config_sp); //------------------------------------------------------------------ /// Broadcasts the given structured data object from the given /// plugin. /// /// StructuredDataPlugin instances can use this to optionally /// broadcast any of their data if they want to make it available /// for clients. The data will come in on the structured data /// event bit (eBroadcastBitStructuredData). /// /// @param[in] object_sp /// The structured data object to broadcast. /// /// @param[in] plugin_sp /// The plugin that will be reported in the event's plugin /// parameter. //------------------------------------------------------------------ void BroadcastStructuredData(const StructuredData::ObjectSP &object_sp, const lldb::StructuredDataPluginSP &plugin_sp); //------------------------------------------------------------------ /// Returns the StructuredDataPlugin associated with a given type /// name, if there is one. /// /// There will only be a plugin for a given StructuredDataType if the /// debugged process monitor claims that the feature is supported. /// This is one way to tell whether a feature is available. /// /// @return /// The plugin if one is available for the specified feature; /// otherwise, returns an empty shared pointer. //------------------------------------------------------------------ lldb::StructuredDataPluginSP GetStructuredDataPlugin(const ConstString &type_name) const; //------------------------------------------------------------------ /// Starts tracing with the configuration provided in options. To /// enable tracing on the complete process the thread_id in the /// options should be set to LLDB_INVALID_THREAD_ID. The API returns /// a user_id which is needed by other API's that manipulate the /// trace instance. /// The handling of erroneous or unsupported configuration is left /// to the trace technology implementations in the server, as they /// could be returned as an error, or rounded to a valid /// configuration to start tracing. In the later case the /// GetTraceConfig should supply the actual used trace /// configuration. //------------------------------------------------------------------ - virtual lldb::user_id_t StartTrace(lldb::TraceOptionsSP &options, + virtual lldb::user_id_t StartTrace(const TraceOptions &options, Status &error) { error.SetErrorString("Not implemented"); return LLDB_INVALID_UID; } //------------------------------------------------------------------ /// Stops the tracing instance leading to deletion of the trace /// data. The tracing instance is identified by the user_id which /// is obtained when tracing was started from the StartTrace. /// In case tracing of the complete process needs to be stopped /// the thread_id should be set to LLDB_INVALID_THREAD_ID. /// In the other case that tracing on an individual thread needs /// to be stopped a thread_id can be supplied. //------------------------------------------------------------------ - virtual void StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id, - Status &error) { - error.SetErrorString("Not implemented"); + virtual Status StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) { + return Status("Not implemented"); } //------------------------------------------------------------------ /// Provides the trace data as raw bytes. A buffer needs to be /// supplied to copy the trace data. The exact behavior of this API /// may vary across trace technology, as some may support partial /// reading of the trace data from a specified offset while some /// may not. The thread_id should be used to select a particular /// thread for trace extraction. //------------------------------------------------------------------ - virtual size_t GetData(lldb::user_id_t uid, lldb::tid_t thread_id, - Status &error, void *buf, size_t size, + virtual Status GetData(lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, size_t offset = 0) { - error.SetErrorString("Not implemented"); - return 0; + return Status("Not implemented"); } //------------------------------------------------------------------ /// Similar API as above except for obtaining meta data //------------------------------------------------------------------ - virtual size_t GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id, - Status &error, void *buf, size_t size, + virtual Status GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, size_t offset = 0) { - error.SetErrorString("Not implemented"); - return 0; + return Status("Not implemented"); } //------------------------------------------------------------------ /// API to obtain the trace configuration used by a trace instance. /// Configurations that may be specific to some trace technology /// should be stored in the custom parameters. The options are /// transported to the server, which shall interpret accordingly. /// The thread_id can be specified in the options to obtain the /// configuration used by a specific thread. The thread_id specified /// should also match the uid otherwise an error will be returned. //------------------------------------------------------------------ - virtual void GetTraceConfig(lldb::user_id_t uid, Status &error, - lldb::TraceOptionsSP &options) { - error.SetErrorString("Not implemented"); - return; + virtual Status GetTraceConfig(lldb::user_id_t uid, TraceOptions &options) { + return Status("Not implemented"); } protected: void SetState(lldb::EventSP &event_sp); lldb::StateType GetPrivateState(); //------------------------------------------------------------------ /// The "private" side of resuming a process. This doesn't alter the /// state of m_run_lock, but just causes the process to resume. /// /// @return /// An Status object describing the success or failure of the resume. //------------------------------------------------------------------ Status PrivateResume(); //------------------------------------------------------------------ // Called internally //------------------------------------------------------------------ void CompleteAttach(); //------------------------------------------------------------------ /// Print a user-visible warning one time per Process /// /// A facility for printing a warning to the user once per repeat_key. /// /// warning_type is from the Process::Warnings enums. /// repeat_key is a pointer value that will be used to ensure that the /// warning message is not printed multiple times. For instance, with a /// warning about a function being optimized, you can pass the CompileUnit /// pointer to have the warning issued for only the first function in a /// CU, or the Function pointer to have it issued once for every function, /// or a Module pointer to have it issued once per Module. /// /// Classes outside Process should call a specific PrintWarning method /// so that the warning strings are all centralized in Process, instead of /// calling PrintWarning() directly. /// /// @param [in] warning_type /// One of the types defined in Process::Warnings. /// /// @param [in] repeat_key /// A pointer value used to ensure that the warning is only printed once. /// May be nullptr, indicating that the warning is printed unconditionally /// every time. /// /// @param [in] fmt /// printf style format string //------------------------------------------------------------------ void PrintWarning(uint64_t warning_type, const void *repeat_key, const char *fmt, ...) __attribute__((format(printf, 4, 5))); //------------------------------------------------------------------ // NextEventAction provides a way to register an action on the next // event that is delivered to this process. There is currently only // one next event action allowed in the process at one time. If a // new "NextEventAction" is added while one is already present, the // old action will be discarded (with HandleBeingUnshipped called // after it is discarded.) // // If you want to resume the process as a result of a resume action, // call RequestResume, don't call Resume directly. //------------------------------------------------------------------ class NextEventAction { public: typedef enum EventActionResult { eEventActionSuccess, eEventActionRetry, eEventActionExit } EventActionResult; NextEventAction(Process *process) : m_process(process) {} virtual ~NextEventAction() = default; virtual EventActionResult PerformAction(lldb::EventSP &event_sp) = 0; virtual void HandleBeingUnshipped() {} virtual EventActionResult HandleBeingInterrupted() = 0; virtual const char *GetExitString() = 0; void RequestResume() { m_process->m_resume_requested = true; } protected: Process *m_process; }; void SetNextEventAction(Process::NextEventAction *next_event_action) { if (m_next_event_action_ap.get()) m_next_event_action_ap->HandleBeingUnshipped(); m_next_event_action_ap.reset(next_event_action); } // This is the completer for Attaching: class AttachCompletionHandler : public NextEventAction { public: AttachCompletionHandler(Process *process, uint32_t exec_count); ~AttachCompletionHandler() override = default; EventActionResult PerformAction(lldb::EventSP &event_sp) override; EventActionResult HandleBeingInterrupted() override; const char *GetExitString() override; private: uint32_t m_exec_count; std::string m_exit_string; }; bool PrivateStateThreadIsValid() const { lldb::StateType state = m_private_state.GetValue(); return state != lldb::eStateInvalid && state != lldb::eStateDetached && state != lldb::eStateExited && m_private_state_thread.IsJoinable(); } void ForceNextEventDelivery() { m_force_next_event_delivery = true; } //------------------------------------------------------------------ /// Loads any plugins associated with asynchronous structured data /// and maps the relevant supported type name to the plugin. /// /// Processes can receive asynchronous structured data from the /// process monitor. This method will load and map any structured /// data plugins that support the given set of supported type names. /// Later, if any of these features are enabled, the process monitor /// is free to generate asynchronous structured data. The data must /// come in as a single \b StructuredData::Dictionary. That dictionary /// must have a string field named 'type', with a value that equals /// the relevant type name string (one of the values in /// \b supported_type_names). /// /// @param[in] supported_type_names /// An array of zero or more type names. Each must be unique. /// For each entry in the list, a StructuredDataPlugin will be /// searched for that supports the structured data type name. //------------------------------------------------------------------ void MapSupportedStructuredDataPlugins( const StructuredData::Array &supported_type_names); //------------------------------------------------------------------ /// Route the incoming structured data dictionary to the right plugin. /// /// The incoming structured data must be a dictionary, and it must /// have a key named 'type' that stores a string value. The string /// value must be the name of the structured data feature that /// knows how to handle it. /// /// @param[in] object_sp /// When non-null and pointing to a dictionary, the 'type' /// key's string value is used to look up the plugin that /// was registered for that structured data type. It then /// calls the following method on the StructuredDataPlugin /// instance: /// /// virtual void /// HandleArrivalOfStructuredData(Process &process, /// const ConstString &type_name, /// const StructuredData::ObjectSP /// &object_sp) /// /// @return /// True if the structured data was routed to a plugin; otherwise, /// false. //------------------------------------------------------------------ bool RouteAsyncStructuredData(const StructuredData::ObjectSP object_sp); //------------------------------------------------------------------ // Type definitions //------------------------------------------------------------------ typedef std::map LanguageRuntimeCollection; typedef std::unordered_set WarningsPointerSet; typedef std::map WarningsCollection; struct PreResumeCallbackAndBaton { bool (*callback)(void *); void *baton; PreResumeCallbackAndBaton(PreResumeActionCallback in_callback, void *in_baton) : callback(in_callback), baton(in_baton) {} bool operator== (const PreResumeCallbackAndBaton &rhs) { return callback == rhs.callback && baton == rhs.baton; } }; using StructuredDataPluginMap = std::map; //------------------------------------------------------------------ // Member variables //------------------------------------------------------------------ std::weak_ptr m_target_sp; ///< The target that owns this process. ThreadSafeValue m_public_state; ThreadSafeValue m_private_state; // The actual state of our process Broadcaster m_private_state_broadcaster; // This broadcaster feeds state // changed events into the private // state thread's listener. Broadcaster m_private_state_control_broadcaster; // This is the control // broadcaster, used to // pause, resume & stop the // private state thread. lldb::ListenerSP m_private_state_listener_sp; // This is the listener for the // private state thread. HostThread m_private_state_thread; ///< Thread ID for the thread that watches ///internal state events ProcessModID m_mod_id; ///< Tracks the state of the process over stops and ///other alterations. uint32_t m_process_unique_id; ///< Each lldb_private::Process class that is ///created gets a unique integer ID that ///increments with each new instance uint32_t m_thread_index_id; ///< Each thread is created with a 1 based index ///that won't get re-used. std::map m_thread_id_to_index_id_map; int m_exit_status; ///< The exit status of the process, or -1 if not set. std::string m_exit_string; ///< A textual description of why a process exited. std::mutex m_exit_status_mutex; ///< Mutex so m_exit_status m_exit_string can ///be safely accessed from multiple threads std::recursive_mutex m_thread_mutex; ThreadList m_thread_list_real; ///< The threads for this process as are known ///to the protocol we are debugging with ThreadList m_thread_list; ///< The threads for this process as the user will ///see them. This is usually the same as ///< m_thread_list_real, but might be different if there is an OS plug-in ///creating memory threads ThreadList m_extended_thread_list; ///< Owner for extended threads that may be ///generated, cleared on natural stops uint32_t m_extended_thread_stop_id; ///< The natural stop id when ///extended_thread_list was last updated QueueList m_queue_list; ///< The list of libdispatch queues at a given stop point uint32_t m_queue_list_stop_id; ///< The natural stop id when queue list was ///last fetched std::vector m_notifications; ///< The list of notifications ///that this process can deliver. std::vector m_image_tokens; lldb::ListenerSP m_listener_sp; ///< Shared pointer to the listener used for ///public events. Can not be empty. BreakpointSiteList m_breakpoint_site_list; ///< This is the list of breakpoint ///locations we intend to insert in ///the target. lldb::DynamicLoaderUP m_dyld_ap; lldb::JITLoaderListUP m_jit_loaders_ap; lldb::DynamicCheckerFunctionsUP m_dynamic_checkers_ap; ///< The functions used ///by the expression ///parser to validate ///data that ///expressions use. lldb::OperatingSystemUP m_os_ap; lldb::SystemRuntimeUP m_system_runtime_ap; lldb::UnixSignalsSP m_unix_signals_sp; /// This is the current signal set for this process. lldb::ABISP m_abi_sp; lldb::IOHandlerSP m_process_input_reader; Communication m_stdio_communication; std::recursive_mutex m_stdio_communication_mutex; bool m_stdin_forward; /// Remember if stdin must be forwarded to remote debug /// server std::string m_stdout_data; std::string m_stderr_data; std::recursive_mutex m_profile_data_comm_mutex; std::vector m_profile_data; Predicate m_iohandler_sync; MemoryCache m_memory_cache; AllocatedMemoryCache m_allocated_memory_cache; bool m_should_detach; /// Should we detach if the process object goes away /// with an explicit call to Kill or Detach? LanguageRuntimeCollection m_language_runtimes; InstrumentationRuntimeCollection m_instrumentation_runtimes; std::unique_ptr m_next_event_action_ap; std::vector m_pre_resume_actions; ProcessRunLock m_public_run_lock; ProcessRunLock m_private_run_lock; ArchSpec::StopInfoOverrideCallbackType m_stop_info_override_callback; bool m_currently_handling_do_on_removals; bool m_resume_requested; // If m_currently_handling_event or // m_currently_handling_do_on_removals are true, // Resume will only request a resume, using this flag // to check. bool m_finalizing; // This is set at the beginning of Process::Finalize() to // stop functions from looking up or creating things during // a finalize call bool m_finalize_called; // This is set at the end of Process::Finalize() bool m_clear_thread_plans_on_stop; bool m_force_next_event_delivery; lldb::StateType m_last_broadcast_state; /// This helps with the Public event /// coalescing in /// ShouldBroadcastEvent. std::map m_resolved_indirect_addresses; bool m_destroy_in_process; bool m_can_interpret_function_calls; // Some targets, e.g the OSX kernel, // don't support the ability to modify // the stack. WarningsCollection m_warnings_issued; // A set of object pointers which have // already had warnings printed std::mutex m_run_thread_plan_lock; StructuredDataPluginMap m_structured_data_plugin_map; enum { eCanJITDontKnow = 0, eCanJITYes, eCanJITNo } m_can_jit; size_t RemoveBreakpointOpcodesFromBuffer(lldb::addr_t addr, size_t size, uint8_t *buf) const; void SynchronouslyNotifyStateChanged(lldb::StateType state); void SetPublicState(lldb::StateType new_state, bool restarted); void SetPrivateState(lldb::StateType state); bool StartPrivateStateThread(bool is_secondary_thread = false); void StopPrivateStateThread(); void PausePrivateStateThread(); void ResumePrivateStateThread(); private: struct PrivateStateThreadArgs { PrivateStateThreadArgs(Process *p, bool s) : process(p), is_secondary_thread(s){}; Process *process; bool is_secondary_thread; }; // arg is a pointer to a new'ed PrivateStateThreadArgs structure. // PrivateStateThread will free it for you. static lldb::thread_result_t PrivateStateThread(void *arg); // The starts up the private state thread that will watch for events from the // debugee. // Pass true for is_secondary_thread in the case where you have to temporarily // spin up a // secondary state thread to handle events from a hand-called function on the // primary // private state thread. lldb::thread_result_t RunPrivateStateThread(bool is_secondary_thread); protected: void HandlePrivateEvent(lldb::EventSP &event_sp); Status HaltPrivate(); lldb::StateType WaitForProcessStopPrivate(lldb::EventSP &event_sp, const Timeout &timeout); // This waits for both the state change broadcaster, and the control // broadcaster. // If control_only, it only waits for the control broadcaster. bool GetEventsPrivate(lldb::EventSP &event_sp, const Timeout &timeout, bool control_only); lldb::StateType GetStateChangedEventsPrivate(lldb::EventSP &event_sp, const Timeout &timeout); size_t WriteMemoryPrivate(lldb::addr_t addr, const void *buf, size_t size, Status &error); void AppendSTDOUT(const char *s, size_t len); void AppendSTDERR(const char *s, size_t len); void BroadcastAsyncProfileData(const std::string &one_profile_data); static void STDIOReadThreadBytesReceived(void *baton, const void *src, size_t src_len); bool PushProcessIOHandler(); bool PopProcessIOHandler(); bool ProcessIOHandlerIsActive(); bool ProcessIOHandlerExists() const { return static_cast(m_process_input_reader); } Status StopForDestroyOrDetach(lldb::EventSP &exit_event_sp); virtual Status UpdateAutomaticSignalFiltering(); bool StateChangedIsExternallyHijacked(); void LoadOperatingSystemPlugin(bool flush); private: //------------------------------------------------------------------ /// This is the part of the event handling that for a process event. /// It decides what to do with the event and returns true if the /// event needs to be propagated to the user, and false otherwise. /// If the event is not propagated, this call will most likely set /// the target to executing again. /// There is only one place where this call should be called, /// HandlePrivateEvent. /// Don't call it from anywhere else... /// /// @param[in] event_ptr /// This is the event we are handling. /// /// @return /// Returns \b true if the event should be reported to the /// user, \b false otherwise. //------------------------------------------------------------------ bool ShouldBroadcastEvent(Event *event_ptr); void ControlPrivateStateThread(uint32_t signal); DISALLOW_COPY_AND_ASSIGN(Process); }; } // namespace lldb_private #endif // liblldb_Process_h_ Index: vendor/lldb/dist/include/lldb/Utility/StringExtractor.h =================================================================== --- vendor/lldb/dist/include/lldb/Utility/StringExtractor.h (revision 319149) +++ vendor/lldb/dist/include/lldb/Utility/StringExtractor.h (revision 319150) @@ -1,135 +1,137 @@ //===-- StringExtractor.h ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef utility_StringExtractor_h_ #define utility_StringExtractor_h_ // Other libraries and framework includes // Project includes #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include // for size_t #include #include class StringExtractor { public: enum { BigEndian = 0, LittleEndian = 1 }; //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ StringExtractor(); StringExtractor(llvm::StringRef packet_str); StringExtractor(const char *packet_cstr); StringExtractor(const StringExtractor &rhs); virtual ~StringExtractor(); //------------------------------------------------------------------ // Operators //------------------------------------------------------------------ const StringExtractor &operator=(const StringExtractor &rhs); void Reset(llvm::StringRef str) { m_packet = str; m_index = 0; } // Returns true if the file position is still valid for the data // contained in this string extractor object. bool IsGood() const { return m_index != UINT64_MAX; } uint64_t GetFilePos() const { return m_index; } void SetFilePos(uint32_t idx) { m_index = idx; } void Clear() { m_packet.clear(); m_index = 0; } void SkipSpaces(); std::string &GetStringRef() { return m_packet; } const std::string &GetStringRef() const { return m_packet; } bool Empty() { return m_packet.empty(); } size_t GetBytesLeft() { if (m_index < m_packet.size()) return m_packet.size() - m_index; return 0; } char GetChar(char fail_value = '\0'); char PeekChar(char fail_value = '\0') { const char *cstr = Peek(); if (cstr) return cstr[0]; return fail_value; } int DecodeHexU8(); uint8_t GetHexU8(uint8_t fail_value = 0, bool set_eof_on_fail = true); bool GetHexU8Ex(uint8_t &ch, bool set_eof_on_fail = true); bool GetNameColonValue(llvm::StringRef &name, llvm::StringRef &value); int32_t GetS32(int32_t fail_value, int base = 0); uint32_t GetU32(uint32_t fail_value, int base = 0); int64_t GetS64(int64_t fail_value, int base = 0); uint64_t GetU64(uint64_t fail_value, int base = 0); uint32_t GetHexMaxU32(bool little_endian, uint32_t fail_value); uint64_t GetHexMaxU64(bool little_endian, uint64_t fail_value); size_t GetHexBytes(llvm::MutableArrayRef dest, uint8_t fail_fill_value); size_t GetHexBytesAvail(llvm::MutableArrayRef dest); uint64_t GetHexWithFixedSize(uint32_t byte_size, bool little_endian, uint64_t fail_value); size_t GetHexByteString(std::string &str); size_t GetHexByteStringFixedLength(std::string &str, uint32_t nibble_length); size_t GetHexByteStringTerminatedBy(std::string &str, char terminator); + bool ConsumeFront(const llvm::StringRef &str); + const char *Peek() { if (m_index < m_packet.size()) return m_packet.c_str() + m_index; return nullptr; } protected: bool fail() { m_index = UINT64_MAX; return false; } //------------------------------------------------------------------ // For StringExtractor only //------------------------------------------------------------------ std::string m_packet; // The string in which to extract data. uint64_t m_index; // When extracting data from a packet, this index // will march along as things get extracted. If set // to UINT64_MAX the end of the packet data was // reached when decoding information }; #endif // utility_StringExtractor_h_ Index: vendor/lldb/dist/include/lldb/lldb-enumerations.h =================================================================== --- vendor/lldb/dist/include/lldb/lldb-enumerations.h (revision 319149) +++ vendor/lldb/dist/include/lldb/lldb-enumerations.h (revision 319150) @@ -1,1080 +1,1092 @@ //===-- lldb-enumerations.h -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLDB_lldb_enumerations_h_ #define LLDB_lldb_enumerations_h_ #ifndef SWIG // With MSVC, the default type of an enum is always signed, even if one of the // enumerator values is too large to fit into a signed integer but would // otherwise fit into an unsigned integer. As a result of this, all of LLDB's // flag-style enumerations that specify something like eValueFoo = 1u << 31 // result in negative values. This usually just results in a benign warning, // but in a few places we actually do comparisons on the enum values, which // would cause a real bug. Furthermore, there's no way to silence only this // warning, as it's part of -Wmicrosoft which also catches a whole slew of // other useful issues. // // To make matters worse, early versions of SWIG don't recognize the syntax // of specifying the underlying type of an enum (and Python doesn't care anyway) // so we need a way to specify the underlying type when the enum is being used // from C++ code, but just use a regular enum when swig is pre-processing. #define FLAGS_ENUM(Name) enum Name : unsigned #define FLAGS_ANONYMOUS_ENUM() enum : unsigned #else #define FLAGS_ENUM(Name) enum Name #define FLAGS_ANONYMOUS_ENUM() enum #endif namespace lldb { //---------------------------------------------------------------------- // Process and Thread States //---------------------------------------------------------------------- enum StateType { eStateInvalid = 0, eStateUnloaded, ///< Process is object is valid, but not currently loaded eStateConnected, ///< Process is connected to remote debug services, but not ///launched or attached to anything yet eStateAttaching, ///< Process is currently trying to attach eStateLaunching, ///< Process is in the process of launching eStateStopped, ///< Process or thread is stopped and can be examined. eStateRunning, ///< Process or thread is running and can't be examined. eStateStepping, ///< Process or thread is in the process of stepping and can ///not be examined. eStateCrashed, ///< Process or thread has crashed and can be examined. eStateDetached, ///< Process has been detached and can't be examined. eStateExited, ///< Process has exited and can't be examined. eStateSuspended ///< Process or thread is in a suspended state as far ///< as the debugger is concerned while other processes ///< or threads get the chance to run. }; //---------------------------------------------------------------------- // Launch Flags //---------------------------------------------------------------------- FLAGS_ENUM(LaunchFlags){ eLaunchFlagNone = 0u, eLaunchFlagExec = (1u << 0), ///< Exec when launching and turn the calling ///process into a new process eLaunchFlagDebug = (1u << 1), ///< Stop as soon as the process launches to ///allow the process to be debugged eLaunchFlagStopAtEntry = (1u << 2), ///< Stop at the program entry point ///instead of auto-continuing when ///launching or attaching at entry point eLaunchFlagDisableASLR = (1u << 3), ///< Disable Address Space Layout Randomization eLaunchFlagDisableSTDIO = (1u << 4), ///< Disable stdio for inferior process (e.g. for a GUI app) eLaunchFlagLaunchInTTY = (1u << 5), ///< Launch the process in a new TTY if supported by the host eLaunchFlagLaunchInShell = (1u << 6), ///< Launch the process inside a shell to get shell expansion eLaunchFlagLaunchInSeparateProcessGroup = (1u << 7), ///< Launch the process in a separate process group eLaunchFlagDontSetExitStatus = (1u << 8), ///< If you are going to hand the ///process off (e.g. to ///debugserver) ///< set this flag so lldb & the handee don't race to set its exit status. eLaunchFlagDetachOnError = (1u << 9), ///< If set, then the client stub ///should detach rather than killing ///the debugee ///< if it loses connection with lldb. eLaunchFlagShellExpandArguments = (1u << 10), ///< Perform shell-style argument expansion eLaunchFlagCloseTTYOnExit = (1u << 11), ///< Close the open TTY on exit }; //---------------------------------------------------------------------- // Thread Run Modes //---------------------------------------------------------------------- enum RunMode { eOnlyThisThread, eAllThreads, eOnlyDuringStepping }; //---------------------------------------------------------------------- // Byte ordering definitions //---------------------------------------------------------------------- enum ByteOrder { eByteOrderInvalid = 0, eByteOrderBig = 1, eByteOrderPDP = 2, eByteOrderLittle = 4 }; //---------------------------------------------------------------------- // Register encoding definitions //---------------------------------------------------------------------- enum Encoding { eEncodingInvalid = 0, eEncodingUint, // unsigned integer eEncodingSint, // signed integer eEncodingIEEE754, // float eEncodingVector // vector registers }; //---------------------------------------------------------------------- // Display format definitions //---------------------------------------------------------------------- enum Format { eFormatDefault = 0, eFormatInvalid = 0, eFormatBoolean, eFormatBinary, eFormatBytes, eFormatBytesWithASCII, eFormatChar, eFormatCharPrintable, // Only printable characters, space if not printable eFormatComplex, // Floating point complex type eFormatComplexFloat = eFormatComplex, eFormatCString, // NULL terminated C strings eFormatDecimal, eFormatEnum, eFormatHex, eFormatHexUppercase, eFormatFloat, eFormatOctal, eFormatOSType, // OS character codes encoded into an integer 'PICT' 'text' // etc... eFormatUnicode16, eFormatUnicode32, eFormatUnsigned, eFormatPointer, eFormatVectorOfChar, eFormatVectorOfSInt8, eFormatVectorOfUInt8, eFormatVectorOfSInt16, eFormatVectorOfUInt16, eFormatVectorOfSInt32, eFormatVectorOfUInt32, eFormatVectorOfSInt64, eFormatVectorOfUInt64, eFormatVectorOfFloat16, eFormatVectorOfFloat32, eFormatVectorOfFloat64, eFormatVectorOfUInt128, eFormatComplexInteger, // Integer complex type eFormatCharArray, // Print characters with no single quotes, used for // character arrays that can contain non printable // characters eFormatAddressInfo, // Describe what an address points to (func + offset with // file/line, symbol + offset, data, etc) eFormatHexFloat, // ISO C99 hex float string eFormatInstruction, // Disassemble an opcode eFormatVoid, // Do not print this kNumFormats }; //---------------------------------------------------------------------- // Description levels for "void GetDescription(Stream *, DescriptionLevel)" // calls //---------------------------------------------------------------------- enum DescriptionLevel { eDescriptionLevelBrief = 0, eDescriptionLevelFull, eDescriptionLevelVerbose, eDescriptionLevelInitial, kNumDescriptionLevels }; //---------------------------------------------------------------------- // Script interpreter types //---------------------------------------------------------------------- enum ScriptLanguage { eScriptLanguageNone, eScriptLanguagePython, eScriptLanguageDefault = eScriptLanguagePython, eScriptLanguageUnknown }; //---------------------------------------------------------------------- // Register numbering types // See RegisterContext::ConvertRegisterKindToRegisterNumber to convert // any of these to the lldb internal register numbering scheme // (eRegisterKindLLDB). //---------------------------------------------------------------------- enum RegisterKind { eRegisterKindEHFrame = 0, // the register numbers seen in eh_frame eRegisterKindDWARF, // the register numbers seen DWARF eRegisterKindGeneric, // insn ptr reg, stack ptr reg, etc not specific to any // particular target eRegisterKindProcessPlugin, // num used by the process plugin - e.g. by the // remote gdb-protocol stub program eRegisterKindLLDB, // lldb's internal register numbers kNumRegisterKinds }; //---------------------------------------------------------------------- // Thread stop reasons //---------------------------------------------------------------------- enum StopReason { eStopReasonInvalid = 0, eStopReasonNone, eStopReasonTrace, eStopReasonBreakpoint, eStopReasonWatchpoint, eStopReasonSignal, eStopReasonException, eStopReasonExec, // Program was re-exec'ed eStopReasonPlanComplete, eStopReasonThreadExiting, eStopReasonInstrumentation }; //---------------------------------------------------------------------- // Command Return Status Types //---------------------------------------------------------------------- enum ReturnStatus { eReturnStatusInvalid, eReturnStatusSuccessFinishNoResult, eReturnStatusSuccessFinishResult, eReturnStatusSuccessContinuingNoResult, eReturnStatusSuccessContinuingResult, eReturnStatusStarted, eReturnStatusFailed, eReturnStatusQuit }; //---------------------------------------------------------------------- // The results of expression evaluation: //---------------------------------------------------------------------- enum ExpressionResults { eExpressionCompleted = 0, eExpressionSetupError, eExpressionParseError, eExpressionDiscarded, eExpressionInterrupted, eExpressionHitBreakpoint, eExpressionTimedOut, eExpressionResultUnavailable, eExpressionStoppedForDebug }; //---------------------------------------------------------------------- // Connection Status Types //---------------------------------------------------------------------- enum ConnectionStatus { eConnectionStatusSuccess, // Success eConnectionStatusEndOfFile, // End-of-file encountered eConnectionStatusError, // Check GetError() for details eConnectionStatusTimedOut, // Request timed out eConnectionStatusNoConnection, // No connection eConnectionStatusLostConnection, // Lost connection while connected to a valid // connection eConnectionStatusInterrupted // Interrupted read }; enum ErrorType { eErrorTypeInvalid, eErrorTypeGeneric, ///< Generic errors that can be any value. eErrorTypeMachKernel, ///< Mach kernel error codes. eErrorTypePOSIX, ///< POSIX error codes. eErrorTypeExpression, ///< These are from the ExpressionResults enum. eErrorTypeWin32 ///< Standard Win32 error codes. }; enum ValueType { eValueTypeInvalid = 0, eValueTypeVariableGlobal = 1, // globals variable eValueTypeVariableStatic = 2, // static variable eValueTypeVariableArgument = 3, // function argument variables eValueTypeVariableLocal = 4, // function local variables eValueTypeRegister = 5, // stack frame register value eValueTypeRegisterSet = 6, // A collection of stack frame register values eValueTypeConstResult = 7, // constant result variables eValueTypeVariableThreadLocal = 8 // thread local storage variable }; //---------------------------------------------------------------------- // Token size/granularities for Input Readers //---------------------------------------------------------------------- enum InputReaderGranularity { eInputReaderGranularityInvalid = 0, eInputReaderGranularityByte, eInputReaderGranularityWord, eInputReaderGranularityLine, eInputReaderGranularityAll }; //------------------------------------------------------------------ /// These mask bits allow a common interface for queries that can /// limit the amount of information that gets parsed to only the /// information that is requested. These bits also can indicate what /// actually did get resolved during query function calls. /// /// Each definition corresponds to a one of the member variables /// in this class, and requests that that item be resolved, or /// indicates that the member did get resolved. //------------------------------------------------------------------ FLAGS_ENUM(SymbolContextItem){ eSymbolContextTarget = (1u << 0), ///< Set when \a target is requested from ///a query, or was located in query ///results eSymbolContextModule = (1u << 1), ///< Set when \a module is requested from ///a query, or was located in query ///results eSymbolContextCompUnit = (1u << 2), ///< Set when \a comp_unit is requested ///from a query, or was located in query ///results eSymbolContextFunction = (1u << 3), ///< Set when \a function is requested ///from a query, or was located in query ///results eSymbolContextBlock = (1u << 4), ///< Set when the deepest \a block is ///requested from a query, or was located ///in query results eSymbolContextLineEntry = (1u << 5), ///< Set when \a line_entry is ///requested from a query, or was ///located in query results eSymbolContextSymbol = (1u << 6), ///< Set when \a symbol is requested from ///a query, or was located in query ///results eSymbolContextEverything = ((eSymbolContextSymbol << 1) - 1u), ///< Indicates to try and lookup everything ///up during a routine symbol context ///query. eSymbolContextVariable = (1u << 7) ///< Set when \a global or static ///variable is requested from a query, or ///was located in query results. ///< eSymbolContextVariable is potentially expensive to lookup so it isn't ///included in ///< eSymbolContextEverything which stops it from being used during frame PC ///lookups and ///< many other potential address to symbol context lookups. }; FLAGS_ENUM(Permissions){ePermissionsWritable = (1u << 0), ePermissionsReadable = (1u << 1), ePermissionsExecutable = (1u << 2)}; enum InputReaderAction { eInputReaderActivate, // reader is newly pushed onto the reader stack eInputReaderAsynchronousOutputWritten, // an async output event occurred; the // reader may want to do something eInputReaderReactivate, // reader is on top of the stack again after another // reader was popped off eInputReaderDeactivate, // another reader was pushed on the stack eInputReaderGotToken, // reader got one of its tokens (granularity) eInputReaderInterrupt, // reader received an interrupt signal (probably from a // control-c) eInputReaderEndOfFile, // reader received an EOF char (probably from a // control-d) eInputReaderDone // reader was just popped off the stack and is done }; FLAGS_ENUM(BreakpointEventType){ eBreakpointEventTypeInvalidType = (1u << 0), eBreakpointEventTypeAdded = (1u << 1), eBreakpointEventTypeRemoved = (1u << 2), eBreakpointEventTypeLocationsAdded = (1u << 3), // Locations added doesn't // get sent when the // breakpoint is created eBreakpointEventTypeLocationsRemoved = (1u << 4), eBreakpointEventTypeLocationsResolved = (1u << 5), eBreakpointEventTypeEnabled = (1u << 6), eBreakpointEventTypeDisabled = (1u << 7), eBreakpointEventTypeCommandChanged = (1u << 8), eBreakpointEventTypeConditionChanged = (1u << 9), eBreakpointEventTypeIgnoreChanged = (1u << 10), eBreakpointEventTypeThreadChanged = (1u << 11)}; FLAGS_ENUM(WatchpointEventType){ eWatchpointEventTypeInvalidType = (1u << 0), eWatchpointEventTypeAdded = (1u << 1), eWatchpointEventTypeRemoved = (1u << 2), eWatchpointEventTypeEnabled = (1u << 6), eWatchpointEventTypeDisabled = (1u << 7), eWatchpointEventTypeCommandChanged = (1u << 8), eWatchpointEventTypeConditionChanged = (1u << 9), eWatchpointEventTypeIgnoreChanged = (1u << 10), eWatchpointEventTypeThreadChanged = (1u << 11), eWatchpointEventTypeTypeChanged = (1u << 12)}; //---------------------------------------------------------------------- /// Programming language type. /// /// These enumerations use the same language enumerations as the DWARF /// specification for ease of use and consistency. /// The enum -> string code is in Language.cpp, don't change this /// table without updating that code as well. //---------------------------------------------------------------------- enum LanguageType { eLanguageTypeUnknown = 0x0000, ///< Unknown or invalid language value. eLanguageTypeC89 = 0x0001, ///< ISO C:1989. eLanguageTypeC = 0x0002, ///< Non-standardized C, such as K&R. eLanguageTypeAda83 = 0x0003, ///< ISO Ada:1983. eLanguageTypeC_plus_plus = 0x0004, ///< ISO C++:1998. eLanguageTypeCobol74 = 0x0005, ///< ISO Cobol:1974. eLanguageTypeCobol85 = 0x0006, ///< ISO Cobol:1985. eLanguageTypeFortran77 = 0x0007, ///< ISO Fortran 77. eLanguageTypeFortran90 = 0x0008, ///< ISO Fortran 90. eLanguageTypePascal83 = 0x0009, ///< ISO Pascal:1983. eLanguageTypeModula2 = 0x000a, ///< ISO Modula-2:1996. eLanguageTypeJava = 0x000b, ///< Java. eLanguageTypeC99 = 0x000c, ///< ISO C:1999. eLanguageTypeAda95 = 0x000d, ///< ISO Ada:1995. eLanguageTypeFortran95 = 0x000e, ///< ISO Fortran 95. eLanguageTypePLI = 0x000f, ///< ANSI PL/I:1976. eLanguageTypeObjC = 0x0010, ///< Objective-C. eLanguageTypeObjC_plus_plus = 0x0011, ///< Objective-C++. eLanguageTypeUPC = 0x0012, ///< Unified Parallel C. eLanguageTypeD = 0x0013, ///< D. eLanguageTypePython = 0x0014, ///< Python. // NOTE: The below are DWARF5 constants, subject to change upon // completion of the DWARF5 specification eLanguageTypeOpenCL = 0x0015, ///< OpenCL. eLanguageTypeGo = 0x0016, ///< Go. eLanguageTypeModula3 = 0x0017, ///< Modula 3. eLanguageTypeHaskell = 0x0018, ///< Haskell. eLanguageTypeC_plus_plus_03 = 0x0019, ///< ISO C++:2003. eLanguageTypeC_plus_plus_11 = 0x001a, ///< ISO C++:2011. eLanguageTypeOCaml = 0x001b, ///< OCaml. eLanguageTypeRust = 0x001c, ///< Rust. eLanguageTypeC11 = 0x001d, ///< ISO C:2011. eLanguageTypeSwift = 0x001e, ///< Swift. eLanguageTypeJulia = 0x001f, ///< Julia. eLanguageTypeDylan = 0x0020, ///< Dylan. eLanguageTypeC_plus_plus_14 = 0x0021, ///< ISO C++:2014. eLanguageTypeFortran03 = 0x0022, ///< ISO Fortran 2003. eLanguageTypeFortran08 = 0x0023, ///< ISO Fortran 2008. // Vendor Extensions // Note: Language::GetNameForLanguageType // assumes these can be used as indexes into array language_names, and // Language::SetLanguageFromCString and Language::AsCString // assume these can be used as indexes into array g_languages. eLanguageTypeMipsAssembler = 0x0024, ///< Mips_Assembler. eLanguageTypeExtRenderScript = 0x0025, ///< RenderScript. eNumLanguageTypes }; enum InstrumentationRuntimeType { eInstrumentationRuntimeTypeAddressSanitizer = 0x0000, eInstrumentationRuntimeTypeThreadSanitizer = 0x0001, eNumInstrumentationRuntimeTypes }; enum DynamicValueType { eNoDynamicValues = 0, eDynamicCanRunTarget = 1, eDynamicDontRunTarget = 2 }; enum StopShowColumn { eStopShowColumnAnsiOrCaret = 0, eStopShowColumnAnsi = 1, eStopShowColumnCaret = 2, eStopShowColumnNone = 3 }; enum AccessType { eAccessNone, eAccessPublic, eAccessPrivate, eAccessProtected, eAccessPackage }; enum CommandArgumentType { eArgTypeAddress = 0, eArgTypeAddressOrExpression, eArgTypeAliasName, eArgTypeAliasOptions, eArgTypeArchitecture, eArgTypeBoolean, eArgTypeBreakpointID, eArgTypeBreakpointIDRange, eArgTypeBreakpointName, eArgTypeByteSize, eArgTypeClassName, eArgTypeCommandName, eArgTypeCount, eArgTypeDescriptionVerbosity, eArgTypeDirectoryName, eArgTypeDisassemblyFlavor, eArgTypeEndAddress, eArgTypeExpression, eArgTypeExpressionPath, eArgTypeExprFormat, eArgTypeFilename, eArgTypeFormat, eArgTypeFrameIndex, eArgTypeFullName, eArgTypeFunctionName, eArgTypeFunctionOrSymbol, eArgTypeGDBFormat, eArgTypeHelpText, eArgTypeIndex, eArgTypeLanguage, eArgTypeLineNum, eArgTypeLogCategory, eArgTypeLogChannel, eArgTypeMethod, eArgTypeName, eArgTypeNewPathPrefix, eArgTypeNumLines, eArgTypeNumberPerLine, eArgTypeOffset, eArgTypeOldPathPrefix, eArgTypeOneLiner, eArgTypePath, eArgTypePermissionsNumber, eArgTypePermissionsString, eArgTypePid, eArgTypePlugin, eArgTypeProcessName, eArgTypePythonClass, eArgTypePythonFunction, eArgTypePythonScript, eArgTypeQueueName, eArgTypeRegisterName, eArgTypeRegularExpression, eArgTypeRunArgs, eArgTypeRunMode, eArgTypeScriptedCommandSynchronicity, eArgTypeScriptLang, eArgTypeSearchWord, eArgTypeSelector, eArgTypeSettingIndex, eArgTypeSettingKey, eArgTypeSettingPrefix, eArgTypeSettingVariableName, eArgTypeShlibName, eArgTypeSourceFile, eArgTypeSortOrder, eArgTypeStartAddress, eArgTypeSummaryString, eArgTypeSymbol, eArgTypeThreadID, eArgTypeThreadIndex, eArgTypeThreadName, eArgTypeTypeName, eArgTypeUnsignedInteger, eArgTypeUnixSignal, eArgTypeVarName, eArgTypeValue, eArgTypeWidth, eArgTypeNone, eArgTypePlatform, eArgTypeWatchpointID, eArgTypeWatchpointIDRange, eArgTypeWatchType, eArgRawInput, eArgTypeLastArg // Always keep this entry as the last entry in this // enumeration!! }; //---------------------------------------------------------------------- // Symbol types //---------------------------------------------------------------------- enum SymbolType { eSymbolTypeAny = 0, eSymbolTypeInvalid = 0, eSymbolTypeAbsolute, eSymbolTypeCode, eSymbolTypeResolver, eSymbolTypeData, eSymbolTypeTrampoline, eSymbolTypeRuntime, eSymbolTypeException, eSymbolTypeSourceFile, eSymbolTypeHeaderFile, eSymbolTypeObjectFile, eSymbolTypeCommonBlock, eSymbolTypeBlock, eSymbolTypeLocal, eSymbolTypeParam, eSymbolTypeVariable, eSymbolTypeVariableType, eSymbolTypeLineEntry, eSymbolTypeLineHeader, eSymbolTypeScopeBegin, eSymbolTypeScopeEnd, eSymbolTypeAdditional, // When symbols take more than one entry, the extra // entries get this type eSymbolTypeCompiler, eSymbolTypeInstrumentation, eSymbolTypeUndefined, eSymbolTypeObjCClass, eSymbolTypeObjCMetaClass, eSymbolTypeObjCIVar, eSymbolTypeReExported }; enum SectionType { eSectionTypeInvalid, eSectionTypeCode, eSectionTypeContainer, // The section contains child sections eSectionTypeData, eSectionTypeDataCString, // Inlined C string data eSectionTypeDataCStringPointers, // Pointers to C string data eSectionTypeDataSymbolAddress, // Address of a symbol in the symbol table eSectionTypeData4, eSectionTypeData8, eSectionTypeData16, eSectionTypeDataPointers, eSectionTypeDebug, eSectionTypeZeroFill, eSectionTypeDataObjCMessageRefs, // Pointer to function pointer + selector eSectionTypeDataObjCCFStrings, // Objective C const CFString/NSString objects eSectionTypeDWARFDebugAbbrev, eSectionTypeDWARFDebugAddr, eSectionTypeDWARFDebugAranges, eSectionTypeDWARFDebugFrame, eSectionTypeDWARFDebugInfo, eSectionTypeDWARFDebugLine, eSectionTypeDWARFDebugLoc, eSectionTypeDWARFDebugMacInfo, eSectionTypeDWARFDebugMacro, eSectionTypeDWARFDebugPubNames, eSectionTypeDWARFDebugPubTypes, eSectionTypeDWARFDebugRanges, eSectionTypeDWARFDebugStr, eSectionTypeDWARFDebugStrOffsets, eSectionTypeDWARFAppleNames, eSectionTypeDWARFAppleTypes, eSectionTypeDWARFAppleNamespaces, eSectionTypeDWARFAppleObjC, eSectionTypeELFSymbolTable, // Elf SHT_SYMTAB section eSectionTypeELFDynamicSymbols, // Elf SHT_DYNSYM section eSectionTypeELFRelocationEntries, // Elf SHT_REL or SHT_REL section eSectionTypeELFDynamicLinkInfo, // Elf SHT_DYNAMIC section eSectionTypeEHFrame, eSectionTypeARMexidx, eSectionTypeARMextab, eSectionTypeCompactUnwind, // compact unwind section in Mach-O, // __TEXT,__unwind_info eSectionTypeGoSymtab, eSectionTypeAbsoluteAddress, // Dummy section for symbols with absolute // address eSectionTypeOther }; FLAGS_ENUM(EmulateInstructionOptions){ eEmulateInstructionOptionNone = (0u), eEmulateInstructionOptionAutoAdvancePC = (1u << 0), eEmulateInstructionOptionIgnoreConditions = (1u << 1)}; FLAGS_ENUM(FunctionNameType){ eFunctionNameTypeNone = 0u, eFunctionNameTypeAuto = (1u << 1), // Automatically figure out which FunctionNameType // bits to set based on the function name. eFunctionNameTypeFull = (1u << 2), // The function name. // For C this is the same as just the name of the function // For C++ this is the mangled or demangled version of the mangled name. // For ObjC this is the full function signature with the + or // - and the square brackets and the class and selector eFunctionNameTypeBase = (1u << 3), // The function name only, no namespaces // or arguments and no class // methods or selectors will be searched. eFunctionNameTypeMethod = (1u << 4), // Find function by method name (C++) // with no namespace or arguments eFunctionNameTypeSelector = (1u << 5), // Find function by selector name (ObjC) names eFunctionNameTypeAny = eFunctionNameTypeAuto // DEPRECATED: use eFunctionNameTypeAuto }; //---------------------------------------------------------------------- // Basic types enumeration for the public API SBType::GetBasicType() //---------------------------------------------------------------------- enum BasicType { eBasicTypeInvalid = 0, eBasicTypeVoid = 1, eBasicTypeChar, eBasicTypeSignedChar, eBasicTypeUnsignedChar, eBasicTypeWChar, eBasicTypeSignedWChar, eBasicTypeUnsignedWChar, eBasicTypeChar16, eBasicTypeChar32, eBasicTypeShort, eBasicTypeUnsignedShort, eBasicTypeInt, eBasicTypeUnsignedInt, eBasicTypeLong, eBasicTypeUnsignedLong, eBasicTypeLongLong, eBasicTypeUnsignedLongLong, eBasicTypeInt128, eBasicTypeUnsignedInt128, eBasicTypeBool, eBasicTypeHalf, eBasicTypeFloat, eBasicTypeDouble, eBasicTypeLongDouble, eBasicTypeFloatComplex, eBasicTypeDoubleComplex, eBasicTypeLongDoubleComplex, eBasicTypeObjCID, eBasicTypeObjCClass, eBasicTypeObjCSel, eBasicTypeNullPtr, eBasicTypeOther }; enum TraceType { eTraceTypeNone = 0, // Hardware Trace generated by the processor. eTraceTypeProcessorTrace }; +enum StructuredDataType { + eStructuredDataTypeInvalid = -1, + eStructuredDataTypeNull = 0, + eStructuredDataTypeGeneric, + eStructuredDataTypeArray, + eStructuredDataTypeInteger, + eStructuredDataTypeFloat, + eStructuredDataTypeBoolean, + eStructuredDataTypeString, + eStructuredDataTypeDictionary +}; + FLAGS_ENUM(TypeClass){ eTypeClassInvalid = (0u), eTypeClassArray = (1u << 0), eTypeClassBlockPointer = (1u << 1), eTypeClassBuiltin = (1u << 2), eTypeClassClass = (1u << 3), eTypeClassComplexFloat = (1u << 4), eTypeClassComplexInteger = (1u << 5), eTypeClassEnumeration = (1u << 6), eTypeClassFunction = (1u << 7), eTypeClassMemberPointer = (1u << 8), eTypeClassObjCObject = (1u << 9), eTypeClassObjCInterface = (1u << 10), eTypeClassObjCObjectPointer = (1u << 11), eTypeClassPointer = (1u << 12), eTypeClassReference = (1u << 13), eTypeClassStruct = (1u << 14), eTypeClassTypedef = (1u << 15), eTypeClassUnion = (1u << 16), eTypeClassVector = (1u << 17), // Define the last type class as the MSBit of a 32 bit value eTypeClassOther = (1u << 31), // Define a mask that can be used for any type when finding types eTypeClassAny = (0xffffffffu)}; enum TemplateArgumentKind { eTemplateArgumentKindNull = 0, eTemplateArgumentKindType, eTemplateArgumentKindDeclaration, eTemplateArgumentKindIntegral, eTemplateArgumentKindTemplate, eTemplateArgumentKindTemplateExpansion, eTemplateArgumentKindExpression, eTemplateArgumentKindPack }; //---------------------------------------------------------------------- // Options that can be set for a formatter to alter its behavior // Not all of these are applicable to all formatter types //---------------------------------------------------------------------- FLAGS_ENUM(TypeOptions){eTypeOptionNone = (0u), eTypeOptionCascade = (1u << 0), eTypeOptionSkipPointers = (1u << 1), eTypeOptionSkipReferences = (1u << 2), eTypeOptionHideChildren = (1u << 3), eTypeOptionHideValue = (1u << 4), eTypeOptionShowOneLiner = (1u << 5), eTypeOptionHideNames = (1u << 6), eTypeOptionNonCacheable = (1u << 7), eTypeOptionHideEmptyAggregates = (1u << 8)}; //---------------------------------------------------------------------- // This is the return value for frame comparisons. If you are comparing frame A // to frame B // the following cases arise: // 1) When frame A pushes frame B (or a frame that ends up pushing B) A is Older // than B. // 2) When frame A pushed frame B (or if frame A is on the stack but B is not) A // is Younger than B // 3) When frame A and frame B have the same StackID, they are Equal. // 4) When frame A and frame B have the same immediate parent frame, but are not // equal, the comparison yields // SameParent. // 5) If the two frames are on different threads or processes the comparison is // Invalid // 6) If for some reason we can't figure out what went on, we return Unknown. //---------------------------------------------------------------------- enum FrameComparison { eFrameCompareInvalid, eFrameCompareUnknown, eFrameCompareEqual, eFrameCompareSameParent, eFrameCompareYounger, eFrameCompareOlder }; //---------------------------------------------------------------------- // Address Class // // A way of classifying an address used for disassembling and setting // breakpoints. Many object files can track exactly what parts of their // object files are code, data and other information. This is of course // above and beyond just looking at the section types. For example, code // might contain PC relative data and the object file might be able to // tell us that an address in code is data. //---------------------------------------------------------------------- enum AddressClass { eAddressClassInvalid, eAddressClassUnknown, eAddressClassCode, eAddressClassCodeAlternateISA, eAddressClassData, eAddressClassDebug, eAddressClassRuntime }; //---------------------------------------------------------------------- // File Permissions // // Designed to mimic the unix file permission bits so they can be // used with functions that set 'mode_t' to certain values for // permissions. //---------------------------------------------------------------------- FLAGS_ENUM(FilePermissions){ eFilePermissionsUserRead = (1u << 8), eFilePermissionsUserWrite = (1u << 7), eFilePermissionsUserExecute = (1u << 6), eFilePermissionsGroupRead = (1u << 5), eFilePermissionsGroupWrite = (1u << 4), eFilePermissionsGroupExecute = (1u << 3), eFilePermissionsWorldRead = (1u << 2), eFilePermissionsWorldWrite = (1u << 1), eFilePermissionsWorldExecute = (1u << 0), eFilePermissionsUserRW = (eFilePermissionsUserRead | eFilePermissionsUserWrite | 0), eFileFilePermissionsUserRX = (eFilePermissionsUserRead | 0 | eFilePermissionsUserExecute), eFilePermissionsUserRWX = (eFilePermissionsUserRead | eFilePermissionsUserWrite | eFilePermissionsUserExecute), eFilePermissionsGroupRW = (eFilePermissionsGroupRead | eFilePermissionsGroupWrite | 0), eFilePermissionsGroupRX = (eFilePermissionsGroupRead | 0 | eFilePermissionsGroupExecute), eFilePermissionsGroupRWX = (eFilePermissionsGroupRead | eFilePermissionsGroupWrite | eFilePermissionsGroupExecute), eFilePermissionsWorldRW = (eFilePermissionsWorldRead | eFilePermissionsWorldWrite | 0), eFilePermissionsWorldRX = (eFilePermissionsWorldRead | 0 | eFilePermissionsWorldExecute), eFilePermissionsWorldRWX = (eFilePermissionsWorldRead | eFilePermissionsWorldWrite | eFilePermissionsWorldExecute), eFilePermissionsEveryoneR = (eFilePermissionsUserRead | eFilePermissionsGroupRead | eFilePermissionsWorldRead), eFilePermissionsEveryoneW = (eFilePermissionsUserWrite | eFilePermissionsGroupWrite | eFilePermissionsWorldWrite), eFilePermissionsEveryoneX = (eFilePermissionsUserExecute | eFilePermissionsGroupExecute | eFilePermissionsWorldExecute), eFilePermissionsEveryoneRW = (eFilePermissionsEveryoneR | eFilePermissionsEveryoneW | 0), eFilePermissionsEveryoneRX = (eFilePermissionsEveryoneR | 0 | eFilePermissionsEveryoneX), eFilePermissionsEveryoneRWX = (eFilePermissionsEveryoneR | eFilePermissionsEveryoneW | eFilePermissionsEveryoneX), eFilePermissionsFileDefault = eFilePermissionsUserRW, eFilePermissionsDirectoryDefault = eFilePermissionsUserRWX, }; //---------------------------------------------------------------------- // Queue work item types // // The different types of work that can be enqueued on a libdispatch // aka Grand Central Dispatch (GCD) queue. //---------------------------------------------------------------------- enum QueueItemKind { eQueueItemKindUnknown = 0, eQueueItemKindFunction, eQueueItemKindBlock }; //---------------------------------------------------------------------- // Queue type // libdispatch aka Grand Central Dispatch (GCD) queues can be either serial // (executing on one thread) or concurrent (executing on multiple threads). //---------------------------------------------------------------------- enum QueueKind { eQueueKindUnknown = 0, eQueueKindSerial, eQueueKindConcurrent }; //---------------------------------------------------------------------- // Expression Evaluation Stages // These are the cancellable stages of expression evaluation, passed to the // expression evaluation callback, so that you can interrupt expression // evaluation at the various points in its lifecycle. //---------------------------------------------------------------------- enum ExpressionEvaluationPhase { eExpressionEvaluationParse = 0, eExpressionEvaluationIRGen, eExpressionEvaluationExecution, eExpressionEvaluationComplete }; //---------------------------------------------------------------------- // Watchpoint Kind // Indicates what types of events cause the watchpoint to fire. // Used by Native*Protocol-related classes. //---------------------------------------------------------------------- FLAGS_ENUM(WatchpointKind){eWatchpointKindRead = (1u << 0), eWatchpointKindWrite = (1u << 1)}; enum GdbSignal { eGdbSignalBadAccess = 0x91, eGdbSignalBadInstruction = 0x92, eGdbSignalArithmetic = 0x93, eGdbSignalEmulation = 0x94, eGdbSignalSoftware = 0x95, eGdbSignalBreakpoint = 0x96 }; //---------------------------------------------------------------------- // Used with SBHost::GetPath (lldb::PathType) to find files that are // related to LLDB on the current host machine. Most files are relative // to LLDB or are in known locations. //---------------------------------------------------------------------- enum PathType { ePathTypeLLDBShlibDir, // The directory where the lldb.so (unix) or LLDB // mach-o file in LLDB.framework (MacOSX) exists ePathTypeSupportExecutableDir, // Find LLDB support executable directory // (debugserver, etc) ePathTypeHeaderDir, // Find LLDB header file directory ePathTypePythonDir, // Find Python modules (PYTHONPATH) directory ePathTypeLLDBSystemPlugins, // System plug-ins directory ePathTypeLLDBUserPlugins, // User plug-ins directory ePathTypeLLDBTempSystemDir, // The LLDB temp directory for this system that // will be cleaned up on exit ePathTypeGlobalLLDBTempSystemDir, // The LLDB temp directory for this system, // NOT cleaned up on a process exit. ePathTypeClangDir // Find path to Clang builtin headers }; //---------------------------------------------------------------------- // Kind of member function // Used by the type system //---------------------------------------------------------------------- enum MemberFunctionKind { eMemberFunctionKindUnknown = 0, // Not sure what the type of this is eMemberFunctionKindConstructor, // A function used to create instances eMemberFunctionKindDestructor, // A function used to tear down existing // instances eMemberFunctionKindInstanceMethod, // A function that applies to a specific // instance eMemberFunctionKindStaticMethod // A function that applies to a type rather // than any instance }; //---------------------------------------------------------------------- // String matching algorithm used by SBTarget //---------------------------------------------------------------------- enum MatchType { eMatchTypeNormal, eMatchTypeRegex, eMatchTypeStartsWith }; //---------------------------------------------------------------------- // Bitmask that describes details about a type //---------------------------------------------------------------------- FLAGS_ENUM(TypeFlags){ eTypeHasChildren = (1u << 0), eTypeHasValue = (1u << 1), eTypeIsArray = (1u << 2), eTypeIsBlock = (1u << 3), eTypeIsBuiltIn = (1u << 4), eTypeIsClass = (1u << 5), eTypeIsCPlusPlus = (1u << 6), eTypeIsEnumeration = (1u << 7), eTypeIsFuncPrototype = (1u << 8), eTypeIsMember = (1u << 9), eTypeIsObjC = (1u << 10), eTypeIsPointer = (1u << 11), eTypeIsReference = (1u << 12), eTypeIsStructUnion = (1u << 13), eTypeIsTemplate = (1u << 14), eTypeIsTypedef = (1u << 15), eTypeIsVector = (1u << 16), eTypeIsScalar = (1u << 17), eTypeIsInteger = (1u << 18), eTypeIsFloat = (1u << 19), eTypeIsComplex = (1u << 20), eTypeIsSigned = (1u << 21), eTypeInstanceIsPointer = (1u << 22)}; FLAGS_ENUM(CommandFlags){ //---------------------------------------------------------------------- // eCommandRequiresTarget // // Ensures a valid target is contained in m_exe_ctx prior to executing // the command. If a target doesn't exist or is invalid, the command // will fail and CommandObject::GetInvalidTargetDescription() will be // returned as the error. CommandObject subclasses can override the // virtual function for GetInvalidTargetDescription() to provide custom // strings when needed. //---------------------------------------------------------------------- eCommandRequiresTarget = (1u << 0), //---------------------------------------------------------------------- // eCommandRequiresProcess // // Ensures a valid process is contained in m_exe_ctx prior to executing // the command. If a process doesn't exist or is invalid, the command // will fail and CommandObject::GetInvalidProcessDescription() will be // returned as the error. CommandObject subclasses can override the // virtual function for GetInvalidProcessDescription() to provide custom // strings when needed. //---------------------------------------------------------------------- eCommandRequiresProcess = (1u << 1), //---------------------------------------------------------------------- // eCommandRequiresThread // // Ensures a valid thread is contained in m_exe_ctx prior to executing // the command. If a thread doesn't exist or is invalid, the command // will fail and CommandObject::GetInvalidThreadDescription() will be // returned as the error. CommandObject subclasses can override the // virtual function for GetInvalidThreadDescription() to provide custom // strings when needed. //---------------------------------------------------------------------- eCommandRequiresThread = (1u << 2), //---------------------------------------------------------------------- // eCommandRequiresFrame // // Ensures a valid frame is contained in m_exe_ctx prior to executing // the command. If a frame doesn't exist or is invalid, the command // will fail and CommandObject::GetInvalidFrameDescription() will be // returned as the error. CommandObject subclasses can override the // virtual function for GetInvalidFrameDescription() to provide custom // strings when needed. //---------------------------------------------------------------------- eCommandRequiresFrame = (1u << 3), //---------------------------------------------------------------------- // eCommandRequiresRegContext // // Ensures a valid register context (from the selected frame if there // is a frame in m_exe_ctx, or from the selected thread from m_exe_ctx) // is available from m_exe_ctx prior to executing the command. If a // target doesn't exist or is invalid, the command will fail and // CommandObject::GetInvalidRegContextDescription() will be returned as // the error. CommandObject subclasses can override the virtual function // for GetInvalidRegContextDescription() to provide custom strings when // needed. //---------------------------------------------------------------------- eCommandRequiresRegContext = (1u << 4), //---------------------------------------------------------------------- // eCommandTryTargetAPILock // // Attempts to acquire the target lock if a target is selected in the // command interpreter. If the command object fails to acquire the API // lock, the command will fail with an appropriate error message. //---------------------------------------------------------------------- eCommandTryTargetAPILock = (1u << 5), //---------------------------------------------------------------------- // eCommandProcessMustBeLaunched // // Verifies that there is a launched process in m_exe_ctx, if there // isn't, the command will fail with an appropriate error message. //---------------------------------------------------------------------- eCommandProcessMustBeLaunched = (1u << 6), //---------------------------------------------------------------------- // eCommandProcessMustBePaused // // Verifies that there is a paused process in m_exe_ctx, if there // isn't, the command will fail with an appropriate error message. //---------------------------------------------------------------------- eCommandProcessMustBePaused = (1u << 7)}; //---------------------------------------------------------------------- // Whether a summary should cap how much data it returns to users or not //---------------------------------------------------------------------- enum TypeSummaryCapping { eTypeSummaryCapped = true, eTypeSummaryUncapped = false }; } // namespace lldb #endif // LLDB_lldb_enumerations_h_ Index: vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/unwind_expression/TestUnwindExpression.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/unwind_expression/TestUnwindExpression.py (revision 319149) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/unwind_expression/TestUnwindExpression.py (revision 319150) @@ -1,98 +1,127 @@ """ Test stopping at a breakpoint in an expression, and unwinding from there. """ from __future__ import print_function import unittest2 import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class UnwindFromExpressionTest(TestBase): mydir = TestBase.compute_mydir(__file__) + main_spec = lldb.SBFileSpec("main.cpp", False) - def setUp(self): - # Call super's setUp(). - TestBase.setUp(self) - - @add_test_categories(['pyapi']) - @expectedFailureAll(oslist=["windows"]) - def test_unwind_expression(self): - """Test unwinding from an expression.""" + def build_and_run_to_bkpt(self): self.build() exe = os.path.join(os.getcwd(), "a.out") target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) # Create the breakpoint. - main_spec = lldb.SBFileSpec("main.cpp", False) breakpoint = target.BreakpointCreateBySourceRegex( - "// Set a breakpoint here to get started", main_spec) + "// Set a breakpoint here to get started", self.main_spec) self.assertTrue(breakpoint, VALID_BREAKPOINT) # Launch the process, and do not stop at the entry point. process = target.LaunchSimple( None, None, self.get_process_working_directory()) if not process: self.fail("SBTarget.LaunchProcess() failed") if process.GetState() != lldb.eStateStopped: self.fail("Process should be in the 'stopped' state, " "instead the actual state is: '%s'" % lldbutil.state_type_to_str(process.GetState())) - thread = lldbutil.get_one_thread_stopped_at_breakpoint( + self.thread = lldbutil.get_one_thread_stopped_at_breakpoint( process, breakpoint) self.assertIsNotNone( - thread, "Expected one thread to be stopped at the breakpoint") + self.thread, "Expected one thread to be stopped at the breakpoint") + # Next set a breakpoint in this function, set up Expression options to stop on + # breakpoint hits, and call the function. + self.fun_bkpt = self.target().BreakpointCreateBySourceRegex( + "// Stop inside the function here.", self.main_spec) + self.assertTrue(self.fun_bkpt, VALID_BREAKPOINT) + + + @no_debug_info_test + @expectedFailureAll(bugnumber="llvm.org/pr33164") + def test_conditional_bktp(self): + """ + Test conditional breakpoint handling in the IgnoreBreakpoints = False case + """ + self.build_and_run_to_bkpt() + + self.fun_bkpt.SetCondition("0") # Should not get hit + options = lldb.SBExpressionOptions() + options.SetIgnoreBreakpoints(False) + options.SetUnwindOnError(False) + + main_frame = self.thread.GetFrameAtIndex(0) + val = main_frame.EvaluateExpression("second_function(47)", options) + self.assertTrue( + val.GetError().Success(), + "We did complete the execution.") + self.assertEquals(47, val.GetValueAsSigned()) + + + @add_test_categories(['pyapi']) + @expectedFailureAll(oslist=["windows"]) + def test_unwind_expression(self): + """Test unwinding from an expression.""" + self.build_and_run_to_bkpt() + + # Run test with varying one thread timeouts to also test the halting + # logic in the IgnoreBreakpoints = False case + self.do_unwind_test(self.thread, self.fun_bkpt, 1000) + self.do_unwind_test(self.thread, self.fun_bkpt, 100000) + + def do_unwind_test(self, thread, bkpt, timeout): # # Use Python API to evaluate expressions while stopped in a stack frame. # main_frame = thread.GetFrameAtIndex(0) - # Next set a breakpoint in this function, set up Expression options to stop on - # breakpoint hits, and call the function. - fun_bkpt = target.BreakpointCreateBySourceRegex( - "// Stop inside the function here.", main_spec) - self.assertTrue(fun_bkpt, VALID_BREAKPOINT) options = lldb.SBExpressionOptions() options.SetIgnoreBreakpoints(False) options.SetUnwindOnError(False) + options.SetOneThreadTimeoutInMicroSeconds(timeout) val = main_frame.EvaluateExpression("a_function_to_call()", options) self.assertTrue( val.GetError().Fail(), "We did not complete the execution.") error_str = val.GetError().GetCString() self.assertTrue( "Execution was interrupted, reason: breakpoint" in error_str, "And the reason was right.") thread = lldbutil.get_one_thread_stopped_at_breakpoint( - process, fun_bkpt) + self.process(), bkpt) self.assertTrue( thread.IsValid(), "We are indeed stopped at our breakpoint") # Now unwind the expression, and make sure we got back to where we # started. error = thread.UnwindInnermostExpression() self.assertTrue(error.Success(), "We succeeded in unwinding") cur_frame = thread.GetFrameAtIndex(0) self.assertTrue( cur_frame.IsEqual(main_frame), "We got back to the main frame.") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/unwind_expression/main.cpp =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/unwind_expression/main.cpp (revision 319149) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/unwind_expression/main.cpp (revision 319150) @@ -1,14 +1,22 @@ static int static_value = 0; int a_function_to_call() { static_value++; // Stop inside the function here. return static_value; } +int second_function(int x){ + for(int i=0; i<10; ++i) { + a_function_to_call(); + } + return x; +} + int main (int argc, char const *argv[]) { a_function_to_call(); // Set a breakpoint here to get started + second_function(1); return 0; } Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/main.cpp =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/main.cpp (revision 319149) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/main.cpp (nonexistent) @@ -1,50 +0,0 @@ -#include -#include -#include - -std::mutex mutex; -std::condition_variable cond; - -void * -thread3(void *input) -{ - std::unique_lock lock(mutex); - cond.notify_all(); // Set break point at this line. - return NULL; -} - -void * -thread2(void *input) -{ - std::unique_lock lock(mutex); - cond.notify_all(); - cond.wait(lock); - return NULL; -} - -void * -thread1(void *input) -{ - std::thread thread_2(thread2, nullptr); - thread_2.join(); - - return NULL; -} - -int main() -{ - std::unique_lock lock(mutex); - - std::thread thread_1(thread1, nullptr); - cond.wait(lock); - - std::thread thread_3(thread3, nullptr); - cond.wait(lock); - - lock.unlock(); - - thread_1.join(); - thread_3.join(); - - return 0; -} Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/Makefile =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/Makefile (revision 319149) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/Makefile (nonexistent) @@ -1,5 +0,0 @@ -LEVEL = ../../make - -CXX_SOURCES := main.cpp -ENABLE_THREADS := YES -include $(LEVEL)/Makefile.rules Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/TestNumThreads.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/TestNumThreads.py (revision 319149) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/TestNumThreads.py (nonexistent) @@ -1,61 +0,0 @@ -""" -Test number of threads. -""" - -from __future__ import print_function - - -import os -import time -import lldb -from lldbsuite.test.lldbtest import * -import lldbsuite.test.lldbutil as lldbutil - - -class NumberOfThreadsTestCase(TestBase): - - mydir = TestBase.compute_mydir(__file__) - - def setUp(self): - # Call super's setUp(). - TestBase.setUp(self) - # Find the line number to break inside main(). - self.line = line_number('main.cpp', '// Set break point at this line.') - - def test(self): - """Test number of threads.""" - self.build() - exe = os.path.join(os.getcwd(), "a.out") - self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) - - # This should create a breakpoint with 1 location. - lldbutil.run_break_set_by_file_and_line( - self, "main.cpp", self.line, num_expected_locations=1) - - # The breakpoint list should show 3 locations. - self.expect( - "breakpoint list -f", - "Breakpoint location shown correctly", - substrs=[ - "1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % - self.line]) - - # Run the program. - self.runCmd("run", RUN_SUCCEEDED) - - # Stopped once. - self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, - substrs=["stop reason = breakpoint 1."]) - - # Get the target process - target = self.dbg.GetSelectedTarget() - process = target.GetProcess() - - # Get the number of threads - num_threads = process.GetNumThreads() - - # Using std::thread may involve extra threads, so we assert that there are - # at least 4 rather than exactly 4. - self.assertTrue( - num_threads >= 4, - 'Number of expected threads and actual threads do not match.') Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/Makefile =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/Makefile (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/Makefile (revision 319150) @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/TestNumThreads.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/TestNumThreads.py (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/TestNumThreads.py (revision 319150) @@ -0,0 +1,61 @@ +""" +Test number of threads. +""" + +from __future__ import print_function + + +import os +import time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + + +class NumberOfThreadsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test(self): + """Test number of threads.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint with 1 location. + lldbutil.run_break_set_by_file_and_line( + self, "main.cpp", self.line, num_expected_locations=1) + + # The breakpoint list should show 3 locations. + self.expect( + "breakpoint list -f", + "Breakpoint location shown correctly", + substrs=[ + "1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % + self.line]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # Stopped once. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=["stop reason = breakpoint 1."]) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Using std::thread may involve extra threads, so we assert that there are + # at least 4 rather than exactly 4. + self.assertTrue( + num_threads >= 4, + 'Number of expected threads and actual threads do not match.') Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/TestNumThreads.py ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/main.cpp =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/main.cpp (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/main.cpp (revision 319150) @@ -0,0 +1,50 @@ +#include +#include +#include + +std::mutex mutex; +std::condition_variable cond; + +void * +thread3(void *input) +{ + std::unique_lock lock(mutex); + cond.notify_all(); // Set break point at this line. + return NULL; +} + +void * +thread2(void *input) +{ + std::unique_lock lock(mutex); + cond.notify_all(); + cond.wait(lock); + return NULL; +} + +void * +thread1(void *input) +{ + std::thread thread_2(thread2, nullptr); + thread_2.join(); + + return NULL; +} + +int main() +{ + std::unique_lock lock(mutex); + + std::thread thread_1(thread1, nullptr); + cond.wait(lock); + + std::thread thread_3(thread3, nullptr); + cond.wait(lock); + + lock.unlock(); + + thread_1.join(); + thread_3.join(); + + return 0; +} Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/main.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/python_api/sbstructureddata/TestStructuredDataAPI.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/python_api/sbstructureddata/TestStructuredDataAPI.py (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/python_api/sbstructureddata/TestStructuredDataAPI.py (revision 319150) @@ -0,0 +1,206 @@ +""" +Test some SBStructuredData API. +""" + +from __future__ import print_function + +import os +import re +import time + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestStructuredDataAPI(TestBase): + + mydir = TestBase.compute_mydir(__file__) + NO_DEBUG_INFO_TESTCASE = True + + def test(self): + self.structured_data_api_test() + + def setUp(self): + TestBase.setUp(self) + + @add_test_categories(['pyapi']) + def structured_data_api_test(self): + error = lldb.SBError() + s = lldb.SBStream() + s.Print( + "{\"key_dict\":{\"key_string\":\"STRING\",\"key_int\":3,\"key_float\":2.99,\"key_bool\":true,\"key_array\":[\"23\",\"arr\"]}}") + example = lldb.SBStructuredData() + + # Check SetFromJSON API for dictionaries, integers, floating point + # values, strings and arrays + error = example.SetFromJSON(s) + if not error.Success(): + self.fail("FAILED: " + error.GetCString()) + + # Tests for invalid data type + self.invalid_struct_test(example) + + dict_struct = lldb.SBStructuredData() + dict_struct = example.GetValueForKey("key_dict") + + # Tests for dictionary data type + self.dictionary_struct_test(example) + + # Tests for string data type + self.string_struct_test(dict_struct) + + # Tests for integer data type + self.int_struct_test(dict_struct) + + # Tests for floating point data type + self.double_struct_test(dict_struct) + + # Tests for boolean data type + self.bool_struct_test(dict_struct) + + # Tests for array data type + self.array_struct_test(dict_struct) + + def invalid_struct_test(self, example): + invalid_struct = lldb.SBStructuredData() + invalid_struct = example.GetValueForKey("invalid_key") + if invalid_struct.IsValid(): + self.fail("An invalid object should have been returned") + + # Check Type API + if not invalid_struct.GetType() == lldb.eStructuredDataTypeInvalid: + self.fail("Wrong type returned: " + str(invalid_struct.GetType())) + + def dictionary_struct_test(self, example): + # Check API returning a valid SBStructuredData of 'dictionary' type + dict_struct = lldb.SBStructuredData() + dict_struct = example.GetValueForKey("key_dict") + if not dict_struct.IsValid(): + self.fail("A valid object should have been returned") + + # Check Type API + if not dict_struct.GetType() == lldb.eStructuredDataTypeDictionary: + self.fail("Wrong type returned: " + str(dict_struct.GetType())) + + # Check Size API for 'dictionary' type + if not dict_struct.GetSize() == 5: + self.fail("Wrong no of elements returned: " + + str(dict_struct.GetSize())) + + def string_struct_test(self, dict_struct): + string_struct = lldb.SBStructuredData() + string_struct = dict_struct.GetValueForKey("key_string") + if not string_struct.IsValid(): + self.fail("A valid object should have been returned") + + # Check Type API + if not string_struct.GetType() == lldb.eStructuredDataTypeString: + self.fail("Wrong type returned: " + str(string_struct.GetType())) + + # Check API returning 'string' value + output = string_struct.GetStringValue(25) + if not "STRING" in output: + self.fail("wrong output: " + output) + + # Calling wrong API on a SBStructuredData + # (e.g. getting an integer from a string type structure) + output = string_struct.GetIntegerValue() + if output: + self.fail( + "Valid integer value " + + str(output) + + " returned for a string object") + + def int_struct_test(self, dict_struct): + # Check a valid SBStructuredData containing an 'integer' by + int_struct = lldb.SBStructuredData() + int_struct = dict_struct.GetValueForKey("key_int") + if not int_struct.IsValid(): + self.fail("A valid object should have been returned") + + # Check Type API + if not int_struct.GetType() == lldb.eStructuredDataTypeInteger: + self.fail("Wrong type returned: " + str(int_struct.GetType())) + + # Check API returning 'integer' value + output = int_struct.GetIntegerValue() + if not output == 3: + self.fail("wrong output: " + str(output)) + + # Calling wrong API on a SBStructuredData + # (e.g. getting a string value from an integer type structure) + output = int_struct.GetStringValue(25) + if output: + self.fail( + "Valid string " + + output + + " returned for an integer object") + + def double_struct_test(self, dict_struct): + floating_point_struct = lldb.SBStructuredData() + floating_point_struct = dict_struct.GetValueForKey("key_float") + if not floating_point_struct.IsValid(): + self.fail("A valid object should have been returned") + + # Check Type API + if not floating_point_struct.GetType() == lldb.eStructuredDataTypeFloat: + self.fail("Wrong type returned: " + + str(floating_point_struct.GetType())) + + # Check API returning 'double' value + output = floating_point_struct.GetFloatValue() + if not output == 2.99: + self.fail("wrong output: " + str(output)) + + def bool_struct_test(self, dict_struct): + bool_struct = lldb.SBStructuredData() + bool_struct = dict_struct.GetValueForKey("key_bool") + if not bool_struct.IsValid(): + self.fail("A valid object should have been returned") + + # Check Type API + if not bool_struct.GetType() == lldb.eStructuredDataTypeBoolean: + self.fail("Wrong type returned: " + str(bool_struct.GetType())) + + # Check API returning 'bool' value + output = bool_struct.GetBooleanValue() + if not output: + self.fail("wrong output: " + str(output)) + + def array_struct_test(self, dict_struct): + # Check API returning a valid SBStructuredData of 'array' type + array_struct = lldb.SBStructuredData() + array_struct = dict_struct.GetValueForKey("key_array") + if not array_struct.IsValid(): + self.fail("A valid object should have been returned") + + # Check Type API + if not array_struct.GetType() == lldb.eStructuredDataTypeArray: + self.fail("Wrong type returned: " + str(array_struct.GetType())) + + # Check Size API for 'array' type + if not array_struct.GetSize() == 2: + self.fail("Wrong no of elements returned: " + + str(array_struct.GetSize())) + + # Check API returning a valid SBStructuredData for different 'array' + # indices + string_struct = array_struct.GetItemAtIndex(0) + if not string_struct.IsValid(): + self.fail("A valid object should have been returned") + if not string_struct.GetType() == lldb.eStructuredDataTypeString: + self.fail("Wrong type returned: " + str(string_struct.GetType())) + output = string_struct.GetStringValue(5) + if not output == "23": + self.fail("wrong output: " + str(output)) + + string_struct = array_struct.GetItemAtIndex(1) + if not string_struct.IsValid(): + self.fail("A valid object should have been returned") + if not string_struct.GetType() == lldb.eStructuredDataTypeString: + self.fail("Wrong type returned: " + str(string_struct.GetType())) + output = string_struct.GetStringValue(5) + if not output == "arr": + self.fail("wrong output: " + str(output)) Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/python_api/sbstructureddata/TestStructuredDataAPI.py ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/scripts/interface/SBStructuredData.i =================================================================== --- vendor/lldb/dist/scripts/interface/SBStructuredData.i (revision 319149) +++ vendor/lldb/dist/scripts/interface/SBStructuredData.i (revision 319150) @@ -1,45 +1,62 @@ //===-- SWIG Interface for SBStructuredData ---------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// namespace lldb { %feature("docstring", "A class representing a StructuredData event. This class wraps the event type generated by StructuredData features." ) SBStructuredData; class SBStructuredData { public: - SBStructuredData(); - + SBStructuredData(const lldb::SBStructuredData &rhs); SBStructuredData(const lldb::EventSP &event_sp); ~SBStructuredData(); - + bool IsValid() const; - + void Clear(); + + lldb::SBStructuredData &operator=(const lldb::SBStructuredData &rhs); + + lldb::StructuredDataType GetType() const; + + size_t GetSize() const; + + lldb::SBStructuredData GetValueForKey(const char *key) const; + + lldb::SBStructuredData GetItemAtIndex(size_t idx) const; + + uint64_t GetIntegerValue(uint64_t fail_value = 0) const; + + double GetFloatValue(double fail_value = 0.0) const; + + bool GetBooleanValue(bool fail_value = false) const; + + size_t GetStringValue(char *dst, size_t dst_len) const; lldb::SBError GetAsJSON(lldb::SBStream &stream) const; lldb::SBError GetDescription(lldb::SBStream &stream) const; lldb::SBError SetFromJSON(lldb::SBStream &stream); }; } Index: vendor/lldb/dist/scripts/lldb.swig =================================================================== --- vendor/lldb/dist/scripts/lldb.swig (revision 319149) +++ vendor/lldb/dist/scripts/lldb.swig (revision 319150) @@ -1,209 +1,225 @@ /* lldb.swig This is the input file for SWIG, to create the appropriate C++ wrappers and functions for various scripting languages, to enable them to call the liblldb Script Bridge functions. */ /* Define our module docstring. */ %define DOCSTRING "The lldb module contains the public APIs for Python binding. Some of the important classes are described here: o SBTarget: Represents the target program running under the debugger. o SBProcess: Represents the process associated with the target program. o SBThread: Represents a thread of execution. SBProcess contains SBThread(s). o SBFrame: Represents one of the stack frames associated with a thread. SBThread contains SBFrame(s). o SBSymbolContext: A container that stores various debugger related info. o SBValue: Represents the value of a variable, a register, or an expression. o SBModule: Represents an executable image and its associated object and symbol files. SBTarget contains SBModule(s). o SBBreakpoint: Represents a logical breakpoint and its associated settings. SBTarget contains SBBreakpoint(s). o SBSymbol: Represents the symbol possibly associated with a stack frame. o SBCompileUnit: Represents a compilation unit, or compiled source file. o SBFunction: Represents a generic function, which can be inlined or not. o SBBlock: Represents a lexical block. SBFunction contains SBBlock(s). o SBLineEntry: Specifies an association with a contiguous range of instructions and a source file location. SBCompileUnit contains SBLineEntry(s)." %enddef +/* +Since version 3.0.9, swig's logic for importing the native module has changed in +a way that is incompatible with our usage of the python module as __init__.py +(See swig bug #769). Fortunately, since version 3.0.11, swig provides a way for +us to override the module import logic to suit our needs. This does that. + +Older swig versions will simply ignore this setting. +*/ +%define MODULEIMPORT +"from . import $module" +%enddef +// These versions will not generate working python modules, so error out early. +#if SWIG_VERSION >= 0x030009 && SWIG_VERSION < 0x030011 +#error Swig versions 3.0.9 and 3.0.10 are incompatible with lldb. +#endif + // The name of the module to be created. -%module(docstring=DOCSTRING) lldb +%module(docstring=DOCSTRING, moduleimport=MODULEIMPORT) lldb // Parameter types will be used in the autodoc string. %feature("autodoc", "1"); %pythoncode%{ import uuid import re import os import six %} %include "./Python/python-typemaps.swig" /* C++ headers to be included. */ %{ #include #include %} /* The liblldb header files to be included. */ %{ #include "lldb/lldb-public.h" #include "lldb/API/SBAddress.h" #include "lldb/API/SBAttachInfo.h" #include "lldb/API/SBBlock.h" #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBBreakpointLocation.h" #include "lldb/API/SBBroadcaster.h" #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBCommunication.h" #include "lldb/API/SBCompileUnit.h" #include "lldb/API/SBData.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBDeclaration.h" #include "lldb/API/SBError.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBExecutionContext.h" #include "lldb/API/SBExpressionOptions.h" #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBFileSpecList.h" #include "lldb/API/SBFrame.h" #include "lldb/API/SBFunction.h" #include "lldb/API/SBHostOS.h" #include "lldb/API/SBInstruction.h" #include "lldb/API/SBInstructionList.h" #include "lldb/API/SBLanguageRuntime.h" #include "lldb/API/SBLaunchInfo.h" #include "lldb/API/SBLineEntry.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBMemoryRegionInfo.h" #include "lldb/API/SBMemoryRegionInfoList.h" #include "lldb/API/SBModule.h" #include "lldb/API/SBModuleSpec.h" #include "lldb/API/SBPlatform.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBQueue.h" #include "lldb/API/SBQueueItem.h" #include "lldb/API/SBSection.h" #include "lldb/API/SBSourceManager.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" #include "lldb/API/SBStructuredData.h" #include "lldb/API/SBSymbol.h" #include "lldb/API/SBSymbolContext.h" #include "lldb/API/SBSymbolContextList.h" #include "lldb/API/SBTarget.h" #include "lldb/API/SBThread.h" #include "lldb/API/SBThreadCollection.h" #include "lldb/API/SBThreadPlan.h" #include "lldb/API/SBTrace.h" #include "lldb/API/SBTraceOptions.h" #include "lldb/API/SBType.h" #include "lldb/API/SBTypeCategory.h" #include "lldb/API/SBTypeEnumMember.h" #include "lldb/API/SBTypeFilter.h" #include "lldb/API/SBTypeFormat.h" #include "lldb/API/SBTypeNameSpecifier.h" #include "lldb/API/SBTypeSummary.h" #include "lldb/API/SBTypeSynthetic.h" #include "lldb/API/SBValue.h" #include "lldb/API/SBValueList.h" #include "lldb/API/SBVariablesOptions.h" #include "lldb/API/SBWatchpoint.h" #include "lldb/API/SBUnixSignals.h" #include "../source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h" #include "../scripts/Python/python-swigsafecast.swig" %} /* Various liblldb typedefs that SWIG needs to know about. */ #define __extension__ /* Undefine GCC keyword to make Swig happy when processing glibc's stdint.h. */ /* The ISO C99 standard specifies that in C++ implementations limit macros such as INT32_MAX should only be defined if __STDC_LIMIT_MACROS is. */ #define __STDC_LIMIT_MACROS %include "stdint.i" %include "lldb/lldb-defines.h" %include "lldb/lldb-enumerations.h" %include "lldb/lldb-forward.h" %include "lldb/lldb-types.h" /* Forward declaration of SB classes. */ %include "lldb/API/SBDefines.h" /* Python interface files with docstrings. */ %include "./interface/SBAddress.i" %include "./interface/SBAttachInfo.i" %include "./interface/SBBlock.i" %include "./interface/SBBreakpoint.i" %include "./interface/SBBreakpointLocation.i" %include "./interface/SBBroadcaster.i" %include "./interface/SBCommandInterpreter.i" %include "./interface/SBCommandReturnObject.i" %include "./interface/SBCommunication.i" %include "./interface/SBCompileUnit.i" %include "./interface/SBData.i" %include "./interface/SBDebugger.i" %include "./interface/SBDeclaration.i" %include "./interface/SBError.i" %include "./interface/SBEvent.i" %include "./interface/SBExecutionContext.i" %include "./interface/SBExpressionOptions.i" %include "./interface/SBFileSpec.i" %include "./interface/SBFileSpecList.i" %include "./interface/SBFrame.i" %include "./interface/SBFunction.i" %include "./interface/SBHostOS.i" %include "./interface/SBInstruction.i" %include "./interface/SBInstructionList.i" %include "./interface/SBLanguageRuntime.i" %include "./interface/SBLaunchInfo.i" %include "./interface/SBLineEntry.i" %include "./interface/SBListener.i" %include "./interface/SBMemoryRegionInfo.i" %include "./interface/SBMemoryRegionInfoList.i" %include "./interface/SBModule.i" %include "./interface/SBModuleSpec.i" %include "./interface/SBPlatform.i" %include "./interface/SBProcess.i" %include "./interface/SBQueue.i" %include "./interface/SBQueueItem.i" %include "./interface/SBSection.i" %include "./interface/SBSourceManager.i" %include "./interface/SBStream.i" %include "./interface/SBStringList.i" %include "./interface/SBStructuredData.i" %include "./interface/SBSymbol.i" %include "./interface/SBSymbolContext.i" %include "./interface/SBSymbolContextList.i" %include "./interface/SBTarget.i" %include "./interface/SBThread.i" %include "./interface/SBThreadCollection.i" %include "./interface/SBThreadPlan.i" %include "./interface/SBTrace.i" %include "./interface/SBTraceOptions.i" %include "./interface/SBType.i" %include "./interface/SBTypeCategory.i" %include "./interface/SBTypeEnumMember.i" %include "./interface/SBTypeFilter.i" %include "./interface/SBTypeFormat.i" %include "./interface/SBTypeNameSpecifier.i" %include "./interface/SBTypeSummary.i" %include "./interface/SBTypeSynthetic.i" %include "./interface/SBValue.i" %include "./interface/SBValueList.i" %include "./interface/SBVariablesOptions.i" %include "./interface/SBWatchpoint.i" %include "./interface/SBUnixSignals.i" %include "./Python/python-extensions.swig" %include "./Python/python-wrapper.swig" Index: vendor/lldb/dist/source/API/SBProcess.cpp =================================================================== --- vendor/lldb/dist/source/API/SBProcess.cpp (revision 319149) +++ vendor/lldb/dist/source/API/SBProcess.cpp (revision 319150) @@ -1,1372 +1,1371 @@ //===-- SBProcess.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/API/SBProcess.h" // C Includes #include #include "lldb/lldb-defines.h" #include "lldb/lldb-types.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Interpreter/Args.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Stream.h" // Project includes #include "lldb/API/SBBroadcaster.h" #include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBMemoryRegionInfo.h" #include "lldb/API/SBMemoryRegionInfoList.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" #include "lldb/API/SBStructuredData.h" #include "lldb/API/SBThread.h" #include "lldb/API/SBThreadCollection.h" #include "lldb/API/SBTrace.h" #include "lldb/API/SBTraceOptions.h" #include "lldb/API/SBUnixSignals.h" using namespace lldb; using namespace lldb_private; SBProcess::SBProcess() : m_opaque_wp() {} //---------------------------------------------------------------------- // SBProcess constructor //---------------------------------------------------------------------- SBProcess::SBProcess(const SBProcess &rhs) : m_opaque_wp(rhs.m_opaque_wp) {} SBProcess::SBProcess(const lldb::ProcessSP &process_sp) : m_opaque_wp(process_sp) {} const SBProcess &SBProcess::operator=(const SBProcess &rhs) { if (this != &rhs) m_opaque_wp = rhs.m_opaque_wp; return *this; } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- SBProcess::~SBProcess() {} const char *SBProcess::GetBroadcasterClassName() { return Process::GetStaticBroadcasterClass().AsCString(); } const char *SBProcess::GetPluginName() { ProcessSP process_sp(GetSP()); if (process_sp) { return process_sp->GetPluginName().GetCString(); } return ""; } const char *SBProcess::GetShortPluginName() { ProcessSP process_sp(GetSP()); if (process_sp) { return process_sp->GetPluginName().GetCString(); } return ""; } lldb::ProcessSP SBProcess::GetSP() const { return m_opaque_wp.lock(); } void SBProcess::SetSP(const ProcessSP &process_sp) { m_opaque_wp = process_sp; } void SBProcess::Clear() { m_opaque_wp.reset(); } bool SBProcess::IsValid() const { ProcessSP process_sp(m_opaque_wp.lock()); return ((bool)process_sp && process_sp->IsValid()); } bool SBProcess::RemoteLaunch(char const **argv, char const **envp, const char *stdin_path, const char *stdout_path, const char *stderr_path, const char *working_directory, uint32_t launch_flags, bool stop_at_entry, lldb::SBError &error) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::RemoteLaunch (argv=%p, envp=%p, stdin=%s, " "stdout=%s, stderr=%s, working-dir=%s, launch_flags=0x%x, " "stop_at_entry=%i, &error (%p))...", static_cast(m_opaque_wp.lock().get()), static_cast(argv), static_cast(envp), stdin_path ? stdin_path : "NULL", stdout_path ? stdout_path : "NULL", stderr_path ? stderr_path : "NULL", working_directory ? working_directory : "NULL", launch_flags, stop_at_entry, static_cast(error.get())); ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); if (process_sp->GetState() == eStateConnected) { if (stop_at_entry) launch_flags |= eLaunchFlagStopAtEntry; ProcessLaunchInfo launch_info( FileSpec{stdin_path, false}, FileSpec{stdout_path, false}, FileSpec{stderr_path, false}, FileSpec{working_directory, false}, launch_flags); Module *exe_module = process_sp->GetTarget().GetExecutableModulePointer(); if (exe_module) launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); if (argv) launch_info.GetArguments().AppendArguments(argv); if (envp) launch_info.GetEnvironmentEntries().SetArguments(envp); error.SetError(process_sp->Launch(launch_info)); } else { error.SetErrorString("must be in eStateConnected to call RemoteLaunch"); } } else { error.SetErrorString("unable to attach pid"); } if (log) { SBStream sstr; error.GetDescription(sstr); log->Printf("SBProcess(%p)::RemoteLaunch (...) => SBError (%p): %s", static_cast(process_sp.get()), static_cast(error.get()), sstr.GetData()); } return error.Success(); } bool SBProcess::RemoteAttachToProcessWithID(lldb::pid_t pid, lldb::SBError &error) { ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); if (process_sp->GetState() == eStateConnected) { ProcessAttachInfo attach_info; attach_info.SetProcessID(pid); error.SetError(process_sp->Attach(attach_info)); } else { error.SetErrorString( "must be in eStateConnected to call RemoteAttachToProcessWithID"); } } else { error.SetErrorString("unable to attach pid"); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { SBStream sstr; error.GetDescription(sstr); log->Printf("SBProcess(%p)::RemoteAttachToProcessWithID (%" PRIu64 ") => SBError (%p): %s", static_cast(process_sp.get()), pid, static_cast(error.get()), sstr.GetData()); } return error.Success(); } uint32_t SBProcess::GetNumThreads() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); uint32_t num_threads = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); num_threads = process_sp->GetThreadList().GetSize(can_update); } if (log) log->Printf("SBProcess(%p)::GetNumThreads () => %d", static_cast(process_sp.get()), num_threads); return num_threads; } SBThread SBProcess::GetSelectedThread() const { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->GetThreadList().GetSelectedThread(); sb_thread.SetThread(thread_sp); } if (log) log->Printf("SBProcess(%p)::GetSelectedThread () => SBThread(%p)", static_cast(process_sp.get()), static_cast(thread_sp.get())); return sb_thread; } SBThread SBProcess::CreateOSPluginThread(lldb::tid_t tid, lldb::addr_t context) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->CreateOSPluginThread(tid, context); sb_thread.SetThread(thread_sp); } if (log) log->Printf("SBProcess(%p)::CreateOSPluginThread (tid=0x%" PRIx64 ", context=0x%" PRIx64 ") => SBThread(%p)", static_cast(process_sp.get()), tid, context, static_cast(thread_sp.get())); return sb_thread; } SBTarget SBProcess::GetTarget() const { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBTarget sb_target; TargetSP target_sp; ProcessSP process_sp(GetSP()); if (process_sp) { target_sp = process_sp->GetTarget().shared_from_this(); sb_target.SetSP(target_sp); } if (log) log->Printf("SBProcess(%p)::GetTarget () => SBTarget(%p)", static_cast(process_sp.get()), static_cast(target_sp.get())); return sb_target; } size_t SBProcess::PutSTDIN(const char *src, size_t src_len) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); size_t ret_val = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Status error; ret_val = process_sp->PutSTDIN(src, src_len, error); } if (log) log->Printf("SBProcess(%p)::PutSTDIN (src=\"%s\", src_len=%" PRIu64 ") => %" PRIu64, static_cast(process_sp.get()), src, static_cast(src_len), static_cast(ret_val)); return ret_val; } size_t SBProcess::GetSTDOUT(char *dst, size_t dst_len) const { size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Status error; bytes_read = process_sp->GetSTDOUT(dst, dst_len, error); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::GetSTDOUT (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, static_cast(process_sp.get()), static_cast(bytes_read), dst, static_cast(dst_len), static_cast(bytes_read)); return bytes_read; } size_t SBProcess::GetSTDERR(char *dst, size_t dst_len) const { size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Status error; bytes_read = process_sp->GetSTDERR(dst, dst_len, error); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::GetSTDERR (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, static_cast(process_sp.get()), static_cast(bytes_read), dst, static_cast(dst_len), static_cast(bytes_read)); return bytes_read; } size_t SBProcess::GetAsyncProfileData(char *dst, size_t dst_len) const { size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Status error; bytes_read = process_sp->GetAsyncProfileData(dst, dst_len, error); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::GetAsyncProfileData (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, static_cast(process_sp.get()), static_cast(bytes_read), dst, static_cast(dst_len), static_cast(bytes_read)); return bytes_read; } lldb::SBTrace SBProcess::StartTrace(SBTraceOptions &options, lldb::SBError &error) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); ProcessSP process_sp(GetSP()); error.Clear(); SBTrace trace_instance; trace_instance.SetSP(process_sp); lldb::user_id_t uid = LLDB_INVALID_UID; if (!process_sp) { error.SetErrorString("invalid process"); } else { - - uid = process_sp->StartTrace(options.m_traceoptions_sp, error.ref()); + uid = process_sp->StartTrace(*(options.m_traceoptions_sp), error.ref()); trace_instance.SetTraceUID(uid); - LLDB_LOG(log, "SBProcess::returned uid - %" PRIx64, uid); + LLDB_LOG(log, "SBProcess::returned uid - {0}", uid); } return trace_instance; } void SBProcess::ReportEventState(const SBEvent &event, FILE *out) const { if (out == NULL) return; ProcessSP process_sp(GetSP()); if (process_sp) { const StateType event_state = SBProcess::GetStateFromEvent(event); char message[1024]; int message_len = ::snprintf( message, sizeof(message), "Process %" PRIu64 " %s\n", process_sp->GetID(), SBDebugger::StateAsCString(event_state)); if (message_len > 0) ::fwrite(message, 1, message_len, out); } } void SBProcess::AppendEventStateReport(const SBEvent &event, SBCommandReturnObject &result) { ProcessSP process_sp(GetSP()); if (process_sp) { const StateType event_state = SBProcess::GetStateFromEvent(event); char message[1024]; ::snprintf(message, sizeof(message), "Process %" PRIu64 " %s\n", process_sp->GetID(), SBDebugger::StateAsCString(event_state)); result.AppendMessage(message); } } bool SBProcess::SetSelectedThread(const SBThread &thread) { ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); return process_sp->GetThreadList().SetSelectedThreadByID( thread.GetThreadID()); } return false; } bool SBProcess::SetSelectedThreadByID(lldb::tid_t tid) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); bool ret_val = false; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); ret_val = process_sp->GetThreadList().SetSelectedThreadByID(tid); } if (log) log->Printf("SBProcess(%p)::SetSelectedThreadByID (tid=0x%4.4" PRIx64 ") => %s", static_cast(process_sp.get()), tid, (ret_val ? "true" : "false")); return ret_val; } bool SBProcess::SetSelectedThreadByIndexID(uint32_t index_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); bool ret_val = false; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); ret_val = process_sp->GetThreadList().SetSelectedThreadByIndexID(index_id); } if (log) log->Printf("SBProcess(%p)::SetSelectedThreadByID (tid=0x%x) => %s", static_cast(process_sp.get()), index_id, (ret_val ? "true" : "false")); return ret_val; } SBThread SBProcess::GetThreadAtIndex(size_t index) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->GetThreadList().GetThreadAtIndex(index, can_update); sb_thread.SetThread(thread_sp); } if (log) log->Printf("SBProcess(%p)::GetThreadAtIndex (index=%d) => SBThread(%p)", static_cast(process_sp.get()), static_cast(index), static_cast(thread_sp.get())); return sb_thread; } uint32_t SBProcess::GetNumQueues() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); uint32_t num_queues = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); num_queues = process_sp->GetQueueList().GetSize(); } } if (log) log->Printf("SBProcess(%p)::GetNumQueues () => %d", static_cast(process_sp.get()), num_queues); return num_queues; } SBQueue SBProcess::GetQueueAtIndex(size_t index) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBQueue sb_queue; QueueSP queue_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); queue_sp = process_sp->GetQueueList().GetQueueAtIndex(index); sb_queue.SetQueue(queue_sp); } } if (log) log->Printf("SBProcess(%p)::GetQueueAtIndex (index=%d) => SBQueue(%p)", static_cast(process_sp.get()), static_cast(index), static_cast(queue_sp.get())); return sb_queue; } uint32_t SBProcess::GetStopID(bool include_expression_stops) { ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); if (include_expression_stops) return process_sp->GetStopID(); else return process_sp->GetLastNaturalStopID(); } return 0; } SBEvent SBProcess::GetStopEventForStopID(uint32_t stop_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBEvent sb_event; EventSP event_sp; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); event_sp = process_sp->GetStopEventForStopID(stop_id); sb_event.reset(event_sp); } if (log) log->Printf("SBProcess(%p)::GetStopEventForStopID (stop_id=%" PRIu32 ") => SBEvent(%p)", static_cast(process_sp.get()), stop_id, static_cast(event_sp.get())); return sb_event; } StateType SBProcess::GetState() { StateType ret_val = eStateInvalid; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); ret_val = process_sp->GetState(); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetState () => %s", static_cast(process_sp.get()), lldb_private::StateAsCString(ret_val)); return ret_val; } int SBProcess::GetExitStatus() { int exit_status = 0; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); exit_status = process_sp->GetExitStatus(); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetExitStatus () => %i (0x%8.8x)", static_cast(process_sp.get()), exit_status, exit_status); return exit_status; } const char *SBProcess::GetExitDescription() { const char *exit_desc = NULL; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); exit_desc = process_sp->GetExitDescription(); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetExitDescription () => %s", static_cast(process_sp.get()), exit_desc); return exit_desc; } lldb::pid_t SBProcess::GetProcessID() { lldb::pid_t ret_val = LLDB_INVALID_PROCESS_ID; ProcessSP process_sp(GetSP()); if (process_sp) ret_val = process_sp->GetID(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetProcessID () => %" PRIu64, static_cast(process_sp.get()), ret_val); return ret_val; } uint32_t SBProcess::GetUniqueID() { uint32_t ret_val = 0; ProcessSP process_sp(GetSP()); if (process_sp) ret_val = process_sp->GetUniqueID(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetUniqueID () => %" PRIu32, static_cast(process_sp.get()), ret_val); return ret_val; } ByteOrder SBProcess::GetByteOrder() const { ByteOrder byteOrder = eByteOrderInvalid; ProcessSP process_sp(GetSP()); if (process_sp) byteOrder = process_sp->GetTarget().GetArchitecture().GetByteOrder(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetByteOrder () => %d", static_cast(process_sp.get()), byteOrder); return byteOrder; } uint32_t SBProcess::GetAddressByteSize() const { uint32_t size = 0; ProcessSP process_sp(GetSP()); if (process_sp) size = process_sp->GetTarget().GetArchitecture().GetAddressByteSize(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetAddressByteSize () => %d", static_cast(process_sp.get()), size); return size; } SBError SBProcess::Continue() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBError sb_error; ProcessSP process_sp(GetSP()); if (log) log->Printf("SBProcess(%p)::Continue ()...", static_cast(process_sp.get())); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); if (process_sp->GetTarget().GetDebugger().GetAsyncExecution()) sb_error.ref() = process_sp->Resume(); else sb_error.ref() = process_sp->ResumeSynchronous(NULL); } else sb_error.SetErrorString("SBProcess is invalid"); if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::Continue () => SBError (%p): %s", static_cast(process_sp.get()), static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBError SBProcess::Destroy() { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->Destroy(false)); } else sb_error.SetErrorString("SBProcess is invalid"); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::Destroy () => SBError (%p): %s", static_cast(process_sp.get()), static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBError SBProcess::Stop() { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->Halt()); } else sb_error.SetErrorString("SBProcess is invalid"); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::Stop () => SBError (%p): %s", static_cast(process_sp.get()), static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBError SBProcess::Kill() { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->Destroy(true)); } else sb_error.SetErrorString("SBProcess is invalid"); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::Kill () => SBError (%p): %s", static_cast(process_sp.get()), static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBError SBProcess::Detach() { // FIXME: This should come from a process default. bool keep_stopped = false; return Detach(keep_stopped); } SBError SBProcess::Detach(bool keep_stopped) { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->Detach(keep_stopped)); } else sb_error.SetErrorString("SBProcess is invalid"); return sb_error; } SBError SBProcess::Signal(int signo) { SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->Signal(signo)); } else sb_error.SetErrorString("SBProcess is invalid"); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::Signal (signo=%i) => SBError (%p): %s", static_cast(process_sp.get()), signo, static_cast(sb_error.get()), sstr.GetData()); } return sb_error; } SBUnixSignals SBProcess::GetUnixSignals() { if (auto process_sp = GetSP()) return SBUnixSignals{process_sp}; return {}; } void SBProcess::SendAsyncInterrupt() { ProcessSP process_sp(GetSP()); if (process_sp) { process_sp->SendAsyncInterrupt(); } } SBThread SBProcess::GetThreadByID(tid_t tid) { SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->GetThreadList().FindThreadByID(tid, can_update); sb_thread.SetThread(thread_sp); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetThreadByID (tid=0x%4.4" PRIx64 ") => SBThread (%p)", static_cast(process_sp.get()), tid, static_cast(thread_sp.get())); return sb_thread; } SBThread SBProcess::GetThreadByIndexID(uint32_t index_id) { SBThread sb_thread; ThreadSP thread_sp; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); thread_sp = process_sp->GetThreadList().FindThreadByIndexID(index_id, can_update); sb_thread.SetThread(thread_sp); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetThreadByID (tid=0x%x) => SBThread (%p)", static_cast(process_sp.get()), index_id, static_cast(thread_sp.get())); return sb_thread; } StateType SBProcess::GetStateFromEvent(const SBEvent &event) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); StateType ret_val = Process::ProcessEventData::GetStateFromEvent(event.get()); if (log) log->Printf("SBProcess::GetStateFromEvent (event.sp=%p) => %s", static_cast(event.get()), lldb_private::StateAsCString(ret_val)); return ret_val; } bool SBProcess::GetRestartedFromEvent(const SBEvent &event) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); bool ret_val = Process::ProcessEventData::GetRestartedFromEvent(event.get()); if (log) log->Printf("SBProcess::%s (event.sp=%p) => %d", __FUNCTION__, static_cast(event.get()), ret_val); return ret_val; } size_t SBProcess::GetNumRestartedReasonsFromEvent(const lldb::SBEvent &event) { return Process::ProcessEventData::GetNumRestartedReasons(event.get()); } const char * SBProcess::GetRestartedReasonAtIndexFromEvent(const lldb::SBEvent &event, size_t idx) { return Process::ProcessEventData::GetRestartedReasonAtIndex(event.get(), idx); } SBProcess SBProcess::GetProcessFromEvent(const SBEvent &event) { ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event.get()); if (!process_sp) { // StructuredData events also know the process they come from. // Try that. process_sp = EventDataStructuredData::GetProcessFromEvent(event.get()); } return SBProcess(process_sp); } bool SBProcess::GetInterruptedFromEvent(const SBEvent &event) { return Process::ProcessEventData::GetInterruptedFromEvent(event.get()); } lldb::SBStructuredData SBProcess::GetStructuredDataFromEvent(const lldb::SBEvent &event) { return SBStructuredData(event.GetSP()); } bool SBProcess::EventIsProcessEvent(const SBEvent &event) { return (event.GetBroadcasterClass() == SBProcess::GetBroadcasterClass()) && !EventIsStructuredDataEvent(event); } bool SBProcess::EventIsStructuredDataEvent(const lldb::SBEvent &event) { EventSP event_sp = event.GetSP(); EventData *event_data = event_sp ? event_sp->GetData() : nullptr; return event_data && (event_data->GetFlavor() == EventDataStructuredData::GetFlavorString()); } SBBroadcaster SBProcess::GetBroadcaster() const { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); ProcessSP process_sp(GetSP()); SBBroadcaster broadcaster(process_sp.get(), false); if (log) log->Printf("SBProcess(%p)::GetBroadcaster () => SBBroadcaster (%p)", static_cast(process_sp.get()), static_cast(broadcaster.get())); return broadcaster; } const char *SBProcess::GetBroadcasterClass() { return Process::GetStaticBroadcasterClass().AsCString(); } size_t SBProcess::ReadMemory(addr_t addr, void *dst, size_t dst_len, SBError &sb_error) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (log) log->Printf("SBProcess(%p)::ReadMemory (addr=0x%" PRIx64 ", dst=%p, dst_len=%" PRIu64 ", SBError (%p))...", static_cast(process_sp.get()), addr, static_cast(dst), static_cast(dst_len), static_cast(sb_error.get())); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); bytes_read = process_sp->ReadMemory(addr, dst, dst_len, sb_error.ref()); } else { if (log) log->Printf("SBProcess(%p)::ReadMemory() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::ReadMemory (addr=0x%" PRIx64 ", dst=%p, dst_len=%" PRIu64 ", SBError (%p): %s) => %" PRIu64, static_cast(process_sp.get()), addr, static_cast(dst), static_cast(dst_len), static_cast(sb_error.get()), sstr.GetData(), static_cast(bytes_read)); } return bytes_read; } size_t SBProcess::ReadCStringFromMemory(addr_t addr, void *buf, size_t size, lldb::SBError &sb_error) { size_t bytes_read = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); bytes_read = process_sp->ReadCStringFromMemory(addr, (char *)buf, size, sb_error.ref()); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::ReadCStringFromMemory() => error: process " "is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } return bytes_read; } uint64_t SBProcess::ReadUnsignedFromMemory(addr_t addr, uint32_t byte_size, lldb::SBError &sb_error) { uint64_t value = 0; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); value = process_sp->ReadUnsignedIntegerFromMemory(addr, byte_size, 0, sb_error.ref()); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::ReadUnsignedFromMemory() => error: process " "is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } return value; } lldb::addr_t SBProcess::ReadPointerFromMemory(addr_t addr, lldb::SBError &sb_error) { lldb::addr_t ptr = LLDB_INVALID_ADDRESS; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); ptr = process_sp->ReadPointerFromMemory(addr, sb_error.ref()); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::ReadPointerFromMemory() => error: process " "is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } return ptr; } size_t SBProcess::WriteMemory(addr_t addr, const void *src, size_t src_len, SBError &sb_error) { size_t bytes_written = 0; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); ProcessSP process_sp(GetSP()); if (log) log->Printf("SBProcess(%p)::WriteMemory (addr=0x%" PRIx64 ", src=%p, src_len=%" PRIu64 ", SBError (%p))...", static_cast(process_sp.get()), addr, static_cast(src), static_cast(src_len), static_cast(sb_error.get())); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); bytes_written = process_sp->WriteMemory(addr, src, src_len, sb_error.ref()); } else { if (log) log->Printf("SBProcess(%p)::WriteMemory() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } if (log) { SBStream sstr; sb_error.GetDescription(sstr); log->Printf("SBProcess(%p)::WriteMemory (addr=0x%" PRIx64 ", src=%p, src_len=%" PRIu64 ", SBError (%p): %s) => %" PRIu64, static_cast(process_sp.get()), addr, static_cast(src), static_cast(src_len), static_cast(sb_error.get()), sstr.GetData(), static_cast(bytes_written)); } return bytes_written; } bool SBProcess::GetDescription(SBStream &description) { Stream &strm = description.ref(); ProcessSP process_sp(GetSP()); if (process_sp) { char path[PATH_MAX]; GetTarget().GetExecutable().GetPath(path, sizeof(path)); Module *exe_module = process_sp->GetTarget().GetExecutableModulePointer(); const char *exe_name = NULL; if (exe_module) exe_name = exe_module->GetFileSpec().GetFilename().AsCString(); strm.Printf("SBProcess: pid = %" PRIu64 ", state = %s, threads = %d%s%s", process_sp->GetID(), lldb_private::StateAsCString(GetState()), GetNumThreads(), exe_name ? ", executable = " : "", exe_name ? exe_name : ""); } else strm.PutCString("No value"); return true; } uint32_t SBProcess::GetNumSupportedHardwareWatchpoints(lldb::SBError &sb_error) const { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); uint32_t num = 0; ProcessSP process_sp(GetSP()); if (process_sp) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->GetWatchpointSupportInfo(num)); if (log) log->Printf("SBProcess(%p)::GetNumSupportedHardwareWatchpoints () => %u", static_cast(process_sp.get()), num); } else { sb_error.SetErrorString("SBProcess is invalid"); } return num; } uint32_t SBProcess::LoadImage(lldb::SBFileSpec &sb_remote_image_spec, lldb::SBError &sb_error) { return LoadImage(SBFileSpec(), sb_remote_image_spec, sb_error); } uint32_t SBProcess::LoadImage(const lldb::SBFileSpec &sb_local_image_spec, const lldb::SBFileSpec &sb_remote_image_spec, lldb::SBError &sb_error) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { if (log) log->Printf("SBProcess(%p)::LoadImage() => calling Platform::LoadImage" "for: %s", static_cast(process_sp.get()), sb_local_image_spec.GetFilename()); std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); PlatformSP platform_sp = process_sp->GetTarget().GetPlatform(); return platform_sp->LoadImage(process_sp.get(), *sb_local_image_spec, *sb_remote_image_spec, sb_error.ref()); } else { if (log) log->Printf("SBProcess(%p)::LoadImage() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { if (log) log->Printf("SBProcess(%p)::LoadImage() => error: called with invalid" " process", static_cast(process_sp.get())); sb_error.SetErrorString("process is invalid"); } return LLDB_INVALID_IMAGE_TOKEN; } lldb::SBError SBProcess::UnloadImage(uint32_t image_token) { lldb::SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); PlatformSP platform_sp = process_sp->GetTarget().GetPlatform(); sb_error.SetError( platform_sp->UnloadImage(process_sp.get(), image_token)); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::UnloadImage() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else sb_error.SetErrorString("invalid process"); return sb_error; } lldb::SBError SBProcess::SendEventData(const char *event_data) { lldb::SBError sb_error; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.SetError(process_sp->SendEventData(event_data)); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::SendEventData() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else sb_error.SetErrorString("invalid process"); return sb_error; } uint32_t SBProcess::GetNumExtendedBacktraceTypes() { ProcessSP process_sp(GetSP()); if (process_sp && process_sp->GetSystemRuntime()) { SystemRuntime *runtime = process_sp->GetSystemRuntime(); return runtime->GetExtendedBacktraceTypes().size(); } return 0; } const char *SBProcess::GetExtendedBacktraceTypeAtIndex(uint32_t idx) { ProcessSP process_sp(GetSP()); if (process_sp && process_sp->GetSystemRuntime()) { SystemRuntime *runtime = process_sp->GetSystemRuntime(); const std::vector &names = runtime->GetExtendedBacktraceTypes(); if (idx < names.size()) { return names[idx].AsCString(); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBProcess(%p)::GetExtendedBacktraceTypeAtIndex() => " "error: requested extended backtrace name out of bounds", static_cast(process_sp.get())); } } return NULL; } SBThreadCollection SBProcess::GetHistoryThreads(addr_t addr) { ProcessSP process_sp(GetSP()); SBThreadCollection threads; if (process_sp) { threads = SBThreadCollection(process_sp->GetHistoryThreads(addr)); } return threads; } bool SBProcess::IsInstrumentationRuntimePresent( InstrumentationRuntimeType type) { ProcessSP process_sp(GetSP()); if (!process_sp) return false; InstrumentationRuntimeSP runtime_sp = process_sp->GetInstrumentationRuntime(type); if (!runtime_sp.get()) return false; return runtime_sp->IsActive(); } lldb::SBError SBProcess::SaveCore(const char *file_name) { lldb::SBError error; ProcessSP process_sp(GetSP()); if (!process_sp) { error.SetErrorString("SBProcess is invalid"); return error; } std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); if (process_sp->GetState() != eStateStopped) { error.SetErrorString("the process is not stopped"); return error; } FileSpec core_file(file_name, false); error.ref() = PluginManager::SaveCore(process_sp, core_file); return error; } lldb::SBError SBProcess::GetMemoryRegionInfo(lldb::addr_t load_addr, SBMemoryRegionInfo &sb_region_info) { lldb::SBError sb_error; ProcessSP process_sp(GetSP()); MemoryRegionInfoSP region_info_sp = std::make_shared(); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); sb_error.ref() = process_sp->GetMemoryRegionInfo(load_addr, *region_info_sp); if (sb_error.Success()) { sb_region_info.ref() = *region_info_sp; } } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::GetMemoryRegionInfo() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } return sb_error; } lldb::SBMemoryRegionInfoList SBProcess::GetMemoryRegions() { lldb::SBError sb_error; lldb::SBMemoryRegionInfoList sb_region_list; ProcessSP process_sp(GetSP()); if (process_sp) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process_sp->GetRunLock())) { std::lock_guard guard( process_sp->GetTarget().GetAPIMutex()); std::vector region_list; sb_error.ref() = process_sp->GetMemoryRegions(region_list); if (sb_error.Success()) { std::vector::iterator end = region_list.end(); for (std::vector::iterator it = region_list.begin(); it != end; it++) { SBMemoryRegionInfo sb_region_info(it->get()); sb_region_list.Append(sb_region_info); } } } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBProcess(%p)::GetMemoryRegionInfo() => error: process is running", static_cast(process_sp.get())); sb_error.SetErrorString("process is running"); } } else { sb_error.SetErrorString("SBProcess is invalid"); } return sb_region_list; } Index: vendor/lldb/dist/source/API/SBStructuredData.cpp =================================================================== --- vendor/lldb/dist/source/API/SBStructuredData.cpp (revision 319149) +++ vendor/lldb/dist/source/API/SBStructuredData.cpp (revision 319150) @@ -1,69 +1,111 @@ //===-- SBStructuredData.cpp ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/API/SBStructuredData.h" #include "lldb/API/SBStream.h" #include "lldb/Core/Event.h" #include "lldb/Core/StructuredData.h" #include "lldb/Core/StructuredDataImpl.h" #include "lldb/Target/StructuredDataPlugin.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/Stream.h" using namespace lldb; using namespace lldb_private; #pragma mark-- #pragma mark SBStructuredData SBStructuredData::SBStructuredData() : m_impl_up(new StructuredDataImpl()) {} SBStructuredData::SBStructuredData(const lldb::SBStructuredData &rhs) : m_impl_up(new StructuredDataImpl(*rhs.m_impl_up.get())) {} SBStructuredData::SBStructuredData(const lldb::EventSP &event_sp) : m_impl_up(new StructuredDataImpl(event_sp)) {} SBStructuredData::~SBStructuredData() {} SBStructuredData &SBStructuredData:: operator=(const lldb::SBStructuredData &rhs) { *m_impl_up = *rhs.m_impl_up; return *this; } lldb::SBError SBStructuredData::SetFromJSON(lldb::SBStream &stream) { lldb::SBError error; std::string json_str(stream.GetData()); StructuredData::ObjectSP json_obj = StructuredData::ParseJSON(json_str); m_impl_up->SetObjectSP(json_obj); - if (!json_obj || json_obj->GetType() != StructuredData::Type::eTypeDictionary) + if (!json_obj || json_obj->GetType() != eStructuredDataTypeDictionary) error.SetErrorString("Invalid Syntax"); return error; } bool SBStructuredData::IsValid() const { return m_impl_up->IsValid(); } void SBStructuredData::Clear() { m_impl_up->Clear(); } SBError SBStructuredData::GetAsJSON(lldb::SBStream &stream) const { SBError error; error.SetError(m_impl_up->GetAsJSON(stream.ref())); return error; } lldb::SBError SBStructuredData::GetDescription(lldb::SBStream &stream) const { Status error = m_impl_up->GetDescription(stream.ref()); SBError sb_error; sb_error.SetError(error); return sb_error; +} + +StructuredDataType SBStructuredData::GetType() const { + return (m_impl_up ? m_impl_up->GetType() : eStructuredDataTypeInvalid); +} + +size_t SBStructuredData::GetSize() const { + return (m_impl_up ? m_impl_up->GetSize() : 0); +} + +lldb::SBStructuredData SBStructuredData::GetValueForKey(const char *key) const { + if (!m_impl_up) + return SBStructuredData(); + + SBStructuredData result; + result.m_impl_up->SetObjectSP(m_impl_up->GetValueForKey(key)); + return result; +} + +lldb::SBStructuredData SBStructuredData::GetItemAtIndex(size_t idx) const { + if (!m_impl_up) + return SBStructuredData(); + + SBStructuredData result; + result.m_impl_up->SetObjectSP(m_impl_up->GetItemAtIndex(idx)); + return result; +} + +uint64_t SBStructuredData::GetIntegerValue(uint64_t fail_value) const { + return (m_impl_up ? m_impl_up->GetIntegerValue(fail_value) : fail_value); +} + +double SBStructuredData::GetFloatValue(double fail_value) const { + return (m_impl_up ? m_impl_up->GetFloatValue(fail_value) : fail_value); +} + +bool SBStructuredData::GetBooleanValue(bool fail_value) const { + return (m_impl_up ? m_impl_up->GetBooleanValue(fail_value) : fail_value); +} + +size_t SBStructuredData::GetStringValue(char *dst, size_t dst_len) const { + return (m_impl_up ? m_impl_up->GetStringValue(dst, dst_len) : 0); } Index: vendor/lldb/dist/source/API/SBThread.cpp =================================================================== --- vendor/lldb/dist/source/API/SBThread.cpp (revision 319149) +++ vendor/lldb/dist/source/API/SBThread.cpp (revision 319150) @@ -1,1443 +1,1444 @@ //===-- SBThread.cpp --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/API/SBThread.h" #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBSymbolContext.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/StructuredData.h" #include "lldb/Core/ValueObject.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/Queue.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanStepInRange.h" #include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Target/ThreadPlanStepOut.h" #include "lldb/Target/ThreadPlanStepRange.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/Stream.h" #include "lldb/API/SBAddress.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBFrame.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBThreadCollection.h" #include "lldb/API/SBThreadPlan.h" #include "lldb/API/SBValue.h" +#include "lldb/lldb-enumerations.h" using namespace lldb; using namespace lldb_private; const char *SBThread::GetBroadcasterClassName() { return Thread::GetStaticBroadcasterClass().AsCString(); } //---------------------------------------------------------------------- // Constructors //---------------------------------------------------------------------- SBThread::SBThread() : m_opaque_sp(new ExecutionContextRef()) {} SBThread::SBThread(const ThreadSP &lldb_object_sp) : m_opaque_sp(new ExecutionContextRef(lldb_object_sp)) {} SBThread::SBThread(const SBThread &rhs) : m_opaque_sp(new ExecutionContextRef(*rhs.m_opaque_sp)) {} //---------------------------------------------------------------------- // Assignment operator //---------------------------------------------------------------------- const lldb::SBThread &SBThread::operator=(const SBThread &rhs) { if (this != &rhs) *m_opaque_sp = *rhs.m_opaque_sp; return *this; } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- SBThread::~SBThread() {} lldb::SBQueue SBThread::GetQueue() const { SBQueue sb_queue; QueueSP queue_sp; std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { queue_sp = exe_ctx.GetThreadPtr()->GetQueue(); if (queue_sp) { sb_queue.SetQueue(queue_sp); } } else { if (log) log->Printf("SBThread(%p)::GetQueue() => error: process is running", static_cast(exe_ctx.GetThreadPtr())); } } if (log) log->Printf("SBThread(%p)::GetQueue () => SBQueue(%p)", static_cast(exe_ctx.GetThreadPtr()), static_cast(queue_sp.get())); return sb_queue; } bool SBThread::IsValid() const { std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); Target *target = exe_ctx.GetTargetPtr(); Process *process = exe_ctx.GetProcessPtr(); if (target && process) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&process->GetRunLock())) return m_opaque_sp->GetThreadSP().get() != NULL; } // Without a valid target & process, this thread can't be valid. return false; } void SBThread::Clear() { m_opaque_sp->Clear(); } StopReason SBThread::GetStopReason() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); StopReason reason = eStopReasonInvalid; std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { return exe_ctx.GetThreadPtr()->GetStopReason(); } else { if (log) log->Printf( "SBThread(%p)::GetStopReason() => error: process is running", static_cast(exe_ctx.GetThreadPtr())); } } if (log) log->Printf("SBThread(%p)::GetStopReason () => %s", static_cast(exe_ctx.GetThreadPtr()), Thread::StopReasonAsCString(reason)); return reason; } size_t SBThread::GetStopReasonDataCount() { std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo(); if (stop_info_sp) { StopReason reason = stop_info_sp->GetStopReason(); switch (reason) { case eStopReasonInvalid: case eStopReasonNone: case eStopReasonTrace: case eStopReasonExec: case eStopReasonPlanComplete: case eStopReasonThreadExiting: case eStopReasonInstrumentation: // There is no data for these stop reasons. return 0; case eStopReasonBreakpoint: { break_id_t site_id = stop_info_sp->GetValue(); lldb::BreakpointSiteSP bp_site_sp( exe_ctx.GetProcessPtr()->GetBreakpointSiteList().FindByID( site_id)); if (bp_site_sp) return bp_site_sp->GetNumberOfOwners() * 2; else return 0; // Breakpoint must have cleared itself... } break; case eStopReasonWatchpoint: return 1; case eStopReasonSignal: return 1; case eStopReasonException: return 1; } } } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBThread(%p)::GetStopReasonDataCount() => error: process " "is running", static_cast(exe_ctx.GetThreadPtr())); } } return 0; } uint64_t SBThread::GetStopReasonDataAtIndex(uint32_t idx) { std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { Thread *thread = exe_ctx.GetThreadPtr(); StopInfoSP stop_info_sp = thread->GetStopInfo(); if (stop_info_sp) { StopReason reason = stop_info_sp->GetStopReason(); switch (reason) { case eStopReasonInvalid: case eStopReasonNone: case eStopReasonTrace: case eStopReasonExec: case eStopReasonPlanComplete: case eStopReasonThreadExiting: case eStopReasonInstrumentation: // There is no data for these stop reasons. return 0; case eStopReasonBreakpoint: { break_id_t site_id = stop_info_sp->GetValue(); lldb::BreakpointSiteSP bp_site_sp( exe_ctx.GetProcessPtr()->GetBreakpointSiteList().FindByID( site_id)); if (bp_site_sp) { uint32_t bp_index = idx / 2; BreakpointLocationSP bp_loc_sp( bp_site_sp->GetOwnerAtIndex(bp_index)); if (bp_loc_sp) { if (idx & 1) { // Odd idx, return the breakpoint location ID return bp_loc_sp->GetID(); } else { // Even idx, return the breakpoint ID return bp_loc_sp->GetBreakpoint().GetID(); } } } return LLDB_INVALID_BREAK_ID; } break; case eStopReasonWatchpoint: return stop_info_sp->GetValue(); case eStopReasonSignal: return stop_info_sp->GetValue(); case eStopReasonException: return stop_info_sp->GetValue(); } } } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf("SBThread(%p)::GetStopReasonDataAtIndex() => error: " "process is running", static_cast(exe_ctx.GetThreadPtr())); } } return 0; } bool SBThread::GetStopReasonExtendedInfoAsJSON(lldb::SBStream &stream) { Stream &strm = stream.ref(); std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (!exe_ctx.HasThreadScope()) return false; StopInfoSP stop_info = exe_ctx.GetThreadPtr()->GetStopInfo(); StructuredData::ObjectSP info = stop_info->GetExtendedInfo(); if (!info) return false; info->Dump(strm); return true; } SBThreadCollection SBThread::GetStopReasonExtendedBacktraces(InstrumentationRuntimeType type) { ThreadCollectionSP threads; threads.reset(new ThreadCollection()); // We currently only support ThreadSanitizer. if (type != eInstrumentationRuntimeTypeThreadSanitizer) return threads; std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (!exe_ctx.HasThreadScope()) return threads; ProcessSP process_sp = exe_ctx.GetProcessSP(); StopInfoSP stop_info = exe_ctx.GetThreadPtr()->GetStopInfo(); StructuredData::ObjectSP info = stop_info->GetExtendedInfo(); if (!info) return threads; return process_sp->GetInstrumentationRuntime(type) ->GetBacktracesFromExtendedStopInfo(info); } size_t SBThread::GetStopDescription(char *dst, size_t dst_len) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo(); if (stop_info_sp) { const char *stop_desc = stop_info_sp->GetDescription(); if (stop_desc) { if (log) log->Printf( "SBThread(%p)::GetStopDescription (dst, dst_len) => \"%s\"", static_cast(exe_ctx.GetThreadPtr()), stop_desc); if (dst) return ::snprintf(dst, dst_len, "%s", stop_desc); else { // NULL dst passed in, return the length needed to contain the // description return ::strlen(stop_desc) + 1; // Include the NULL byte for size } } else { size_t stop_desc_len = 0; switch (stop_info_sp->GetStopReason()) { case eStopReasonTrace: case eStopReasonPlanComplete: { static char trace_desc[] = "step"; stop_desc = trace_desc; stop_desc_len = sizeof(trace_desc); // Include the NULL byte for size } break; case eStopReasonBreakpoint: { static char bp_desc[] = "breakpoint hit"; stop_desc = bp_desc; stop_desc_len = sizeof(bp_desc); // Include the NULL byte for size } break; case eStopReasonWatchpoint: { static char wp_desc[] = "watchpoint hit"; stop_desc = wp_desc; stop_desc_len = sizeof(wp_desc); // Include the NULL byte for size } break; case eStopReasonSignal: { stop_desc = exe_ctx.GetProcessPtr()->GetUnixSignals()->GetSignalAsCString( stop_info_sp->GetValue()); if (stop_desc == NULL || stop_desc[0] == '\0') { static char signal_desc[] = "signal"; stop_desc = signal_desc; stop_desc_len = sizeof(signal_desc); // Include the NULL byte for size } } break; case eStopReasonException: { char exc_desc[] = "exception"; stop_desc = exc_desc; stop_desc_len = sizeof(exc_desc); // Include the NULL byte for size } break; case eStopReasonExec: { char exc_desc[] = "exec"; stop_desc = exc_desc; stop_desc_len = sizeof(exc_desc); // Include the NULL byte for size } break; case eStopReasonThreadExiting: { char limbo_desc[] = "thread exiting"; stop_desc = limbo_desc; stop_desc_len = sizeof(limbo_desc); } break; default: break; } if (stop_desc && stop_desc[0]) { if (log) log->Printf( "SBThread(%p)::GetStopDescription (dst, dst_len) => '%s'", static_cast(exe_ctx.GetThreadPtr()), stop_desc); if (dst) return ::snprintf(dst, dst_len, "%s", stop_desc) + 1; // Include the NULL byte if (stop_desc_len == 0) stop_desc_len = ::strlen(stop_desc) + 1; // Include the NULL byte return stop_desc_len; } } } } else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) log->Printf( "SBThread(%p)::GetStopDescription() => error: process is running", static_cast(exe_ctx.GetThreadPtr())); } } if (dst) *dst = 0; return 0; } SBValue SBThread::GetStopReturnValue() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); ValueObjectSP return_valobj_sp; std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo(); if (stop_info_sp) { return_valobj_sp = StopInfo::GetReturnValueObject(stop_info_sp); } } else { if (log) log->Printf( "SBThread(%p)::GetStopReturnValue() => error: process is running", static_cast(exe_ctx.GetThreadPtr())); } } if (log) log->Printf("SBThread(%p)::GetStopReturnValue () => %s", static_cast(exe_ctx.GetThreadPtr()), return_valobj_sp.get() ? return_valobj_sp->GetValueAsCString() : ""); return SBValue(return_valobj_sp); } void SBThread::SetThread(const ThreadSP &lldb_object_sp) { m_opaque_sp->SetThreadSP(lldb_object_sp); } lldb::tid_t SBThread::GetThreadID() const { ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); if (thread_sp) return thread_sp->GetID(); return LLDB_INVALID_THREAD_ID; } uint32_t SBThread::GetIndexID() const { ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); if (thread_sp) return thread_sp->GetIndexID(); return LLDB_INVALID_INDEX32; } const char *SBThread::GetName() const { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); const char *name = NULL; std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { name = exe_ctx.GetThreadPtr()->GetName(); } else { if (log) log->Printf("SBThread(%p)::GetName() => error: process is running", static_cast(exe_ctx.GetThreadPtr())); } } if (log) log->Printf("SBThread(%p)::GetName () => %s", static_cast(exe_ctx.GetThreadPtr()), name ? name : "NULL"); return name; } const char *SBThread::GetQueueName() const { const char *name = NULL; std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { name = exe_ctx.GetThreadPtr()->GetQueueName(); } else { if (log) log->Printf("SBThread(%p)::GetQueueName() => error: process is running", static_cast(exe_ctx.GetThreadPtr())); } } if (log) log->Printf("SBThread(%p)::GetQueueName () => %s", static_cast(exe_ctx.GetThreadPtr()), name ? name : "NULL"); return name; } lldb::queue_id_t SBThread::GetQueueID() const { queue_id_t id = LLDB_INVALID_QUEUE_ID; std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { id = exe_ctx.GetThreadPtr()->GetQueueID(); } else { if (log) log->Printf("SBThread(%p)::GetQueueID() => error: process is running", static_cast(exe_ctx.GetThreadPtr())); } } if (log) log->Printf("SBThread(%p)::GetQueueID () => 0x%" PRIx64, static_cast(exe_ctx.GetThreadPtr()), id); return id; } bool SBThread::GetInfoItemByPathAsString(const char *path, SBStream &strm) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); bool success = false; std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { Thread *thread = exe_ctx.GetThreadPtr(); StructuredData::ObjectSP info_root_sp = thread->GetExtendedInfo(); if (info_root_sp) { StructuredData::ObjectSP node = info_root_sp->GetObjectForDotSeparatedPath(path); if (node) { - if (node->GetType() == StructuredData::Type::eTypeString) { + if (node->GetType() == eStructuredDataTypeString) { strm.Printf("%s", node->GetAsString()->GetValue().str().c_str()); success = true; } - if (node->GetType() == StructuredData::Type::eTypeInteger) { + if (node->GetType() == eStructuredDataTypeInteger) { strm.Printf("0x%" PRIx64, node->GetAsInteger()->GetValue()); success = true; } - if (node->GetType() == StructuredData::Type::eTypeFloat) { + if (node->GetType() == eStructuredDataTypeFloat) { strm.Printf("0x%f", node->GetAsFloat()->GetValue()); success = true; } - if (node->GetType() == StructuredData::Type::eTypeBoolean) { + if (node->GetType() == eStructuredDataTypeBoolean) { if (node->GetAsBoolean()->GetValue() == true) strm.Printf("true"); else strm.Printf("false"); success = true; } - if (node->GetType() == StructuredData::Type::eTypeNull) { + if (node->GetType() == eStructuredDataTypeNull) { strm.Printf("null"); success = true; } } } } else { if (log) log->Printf("SBThread(%p)::GetInfoItemByPathAsString() => error: " "process is running", static_cast(exe_ctx.GetThreadPtr())); } } if (log) log->Printf("SBThread(%p)::GetInfoItemByPathAsString (\"%s\") => \"%s\"", static_cast(exe_ctx.GetThreadPtr()), path, strm.GetData()); return success; } SBError SBThread::ResumeNewPlan(ExecutionContext &exe_ctx, ThreadPlan *new_plan) { SBError sb_error; Process *process = exe_ctx.GetProcessPtr(); if (!process) { sb_error.SetErrorString("No process in SBThread::ResumeNewPlan"); return sb_error; } Thread *thread = exe_ctx.GetThreadPtr(); if (!thread) { sb_error.SetErrorString("No thread in SBThread::ResumeNewPlan"); return sb_error; } // User level plans should be Master Plans so they can be interrupted, other // plans executed, and // then a "continue" will resume the plan. if (new_plan != NULL) { new_plan->SetIsMasterPlan(true); new_plan->SetOkayToDiscard(false); } // Why do we need to set the current thread by ID here??? process->GetThreadList().SetSelectedThreadByID(thread->GetID()); if (process->GetTarget().GetDebugger().GetAsyncExecution()) sb_error.ref() = process->Resume(); else sb_error.ref() = process->ResumeSynchronous(NULL); return sb_error; } void SBThread::StepOver(lldb::RunMode stop_other_threads) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (log) log->Printf("SBThread(%p)::StepOver (stop_other_threads='%s')", static_cast(exe_ctx.GetThreadPtr()), Thread::RunModeAsCString(stop_other_threads)); if (exe_ctx.HasThreadScope()) { Thread *thread = exe_ctx.GetThreadPtr(); bool abort_other_plans = false; StackFrameSP frame_sp(thread->GetStackFrameAtIndex(0)); ThreadPlanSP new_plan_sp; if (frame_sp) { if (frame_sp->HasDebugInformation()) { const LazyBool avoid_no_debug = eLazyBoolCalculate; SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); new_plan_sp = thread->QueueThreadPlanForStepOverRange( abort_other_plans, sc.line_entry, sc, stop_other_threads, avoid_no_debug); } else { new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction( true, abort_other_plans, stop_other_threads); } } // This returns an error, we should use it! ResumeNewPlan(exe_ctx, new_plan_sp.get()); } } void SBThread::StepInto(lldb::RunMode stop_other_threads) { StepInto(NULL, stop_other_threads); } void SBThread::StepInto(const char *target_name, lldb::RunMode stop_other_threads) { SBError error; StepInto(target_name, LLDB_INVALID_LINE_NUMBER, error, stop_other_threads); } void SBThread::StepInto(const char *target_name, uint32_t end_line, SBError &error, lldb::RunMode stop_other_threads) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (log) log->Printf( "SBThread(%p)::StepInto (target_name='%s', stop_other_threads='%s')", static_cast(exe_ctx.GetThreadPtr()), target_name ? target_name : "", Thread::RunModeAsCString(stop_other_threads)); if (exe_ctx.HasThreadScope()) { bool abort_other_plans = false; Thread *thread = exe_ctx.GetThreadPtr(); StackFrameSP frame_sp(thread->GetStackFrameAtIndex(0)); ThreadPlanSP new_plan_sp; if (frame_sp && frame_sp->HasDebugInformation()) { SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); AddressRange range; if (end_line == LLDB_INVALID_LINE_NUMBER) range = sc.line_entry.range; else { if (!sc.GetAddressRangeFromHereToEndLine(end_line, range, error.ref())) return; } const LazyBool step_out_avoids_code_without_debug_info = eLazyBoolCalculate; const LazyBool step_in_avoids_code_without_debug_info = eLazyBoolCalculate; new_plan_sp = thread->QueueThreadPlanForStepInRange( abort_other_plans, range, sc, target_name, stop_other_threads, step_in_avoids_code_without_debug_info, step_out_avoids_code_without_debug_info); } else { new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction( false, abort_other_plans, stop_other_threads); } error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); } } void SBThread::StepOut() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (log) log->Printf("SBThread(%p)::StepOut ()", static_cast(exe_ctx.GetThreadPtr())); if (exe_ctx.HasThreadScope()) { bool abort_other_plans = false; bool stop_other_threads = false; Thread *thread = exe_ctx.GetThreadPtr(); const LazyBool avoid_no_debug = eLazyBoolCalculate; ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepOut( abort_other_plans, NULL, false, stop_other_threads, eVoteYes, eVoteNoOpinion, 0, avoid_no_debug)); // This returns an error, we should use it! ResumeNewPlan(exe_ctx, new_plan_sp.get()); } } void SBThread::StepOutOfFrame(lldb::SBFrame &sb_frame) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (!sb_frame.IsValid()) { if (log) log->Printf( "SBThread(%p)::StepOutOfFrame passed an invalid frame, returning.", static_cast(exe_ctx.GetThreadPtr())); return; } StackFrameSP frame_sp(sb_frame.GetFrameSP()); if (log) { SBStream frame_desc_strm; sb_frame.GetDescription(frame_desc_strm); log->Printf("SBThread(%p)::StepOutOfFrame (frame = SBFrame(%p): %s)", static_cast(exe_ctx.GetThreadPtr()), static_cast(frame_sp.get()), frame_desc_strm.GetData()); } if (exe_ctx.HasThreadScope()) { bool abort_other_plans = false; bool stop_other_threads = false; Thread *thread = exe_ctx.GetThreadPtr(); if (sb_frame.GetThread().GetThreadID() != thread->GetID()) { log->Printf("SBThread(%p)::StepOutOfFrame passed a frame from another " "thread (0x%" PRIx64 " vrs. 0x%" PRIx64 ", returning.", static_cast(exe_ctx.GetThreadPtr()), sb_frame.GetThread().GetThreadID(), thread->GetID()); } ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepOut( abort_other_plans, NULL, false, stop_other_threads, eVoteYes, eVoteNoOpinion, frame_sp->GetFrameIndex())); // This returns an error, we should use it! ResumeNewPlan(exe_ctx, new_plan_sp.get()); } } void SBThread::StepInstruction(bool step_over) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (log) log->Printf("SBThread(%p)::StepInstruction (step_over=%i)", static_cast(exe_ctx.GetThreadPtr()), step_over); if (exe_ctx.HasThreadScope()) { Thread *thread = exe_ctx.GetThreadPtr(); ThreadPlanSP new_plan_sp( thread->QueueThreadPlanForStepSingleInstruction(step_over, true, true)); // This returns an error, we should use it! ResumeNewPlan(exe_ctx, new_plan_sp.get()); } } void SBThread::RunToAddress(lldb::addr_t addr) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (log) log->Printf("SBThread(%p)::RunToAddress (addr=0x%" PRIx64 ")", static_cast(exe_ctx.GetThreadPtr()), addr); if (exe_ctx.HasThreadScope()) { bool abort_other_plans = false; bool stop_other_threads = true; Address target_addr(addr); Thread *thread = exe_ctx.GetThreadPtr(); ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForRunToAddress( abort_other_plans, target_addr, stop_other_threads)); // This returns an error, we should use it! ResumeNewPlan(exe_ctx, new_plan_sp.get()); } } SBError SBThread::StepOverUntil(lldb::SBFrame &sb_frame, lldb::SBFileSpec &sb_file_spec, uint32_t line) { SBError sb_error; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); char path[PATH_MAX]; std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); StackFrameSP frame_sp(sb_frame.GetFrameSP()); if (log) { SBStream frame_desc_strm; sb_frame.GetDescription(frame_desc_strm); sb_file_spec->GetPath(path, sizeof(path)); log->Printf("SBThread(%p)::StepOverUntil (frame = SBFrame(%p): %s, " "file+line = %s:%u)", static_cast(exe_ctx.GetThreadPtr()), static_cast(frame_sp.get()), frame_desc_strm.GetData(), path, line); } if (exe_ctx.HasThreadScope()) { Target *target = exe_ctx.GetTargetPtr(); Thread *thread = exe_ctx.GetThreadPtr(); if (line == 0) { sb_error.SetErrorString("invalid line argument"); return sb_error; } if (!frame_sp) { frame_sp = thread->GetSelectedFrame(); if (!frame_sp) frame_sp = thread->GetStackFrameAtIndex(0); } SymbolContext frame_sc; if (!frame_sp) { sb_error.SetErrorString("no valid frames in thread to step"); return sb_error; } // If we have a frame, get its line frame_sc = frame_sp->GetSymbolContext( eSymbolContextCompUnit | eSymbolContextFunction | eSymbolContextLineEntry | eSymbolContextSymbol); if (frame_sc.comp_unit == NULL) { sb_error.SetErrorStringWithFormat( "frame %u doesn't have debug information", frame_sp->GetFrameIndex()); return sb_error; } FileSpec step_file_spec; if (sb_file_spec.IsValid()) { // The file spec passed in was valid, so use it step_file_spec = sb_file_spec.ref(); } else { if (frame_sc.line_entry.IsValid()) step_file_spec = frame_sc.line_entry.file; else { sb_error.SetErrorString("invalid file argument or no file for frame"); return sb_error; } } // Grab the current function, then we will make sure the "until" address is // within the function. We discard addresses that are out of the current // function, and then if there are no addresses remaining, give an // appropriate // error message. bool all_in_function = true; AddressRange fun_range = frame_sc.function->GetAddressRange(); std::vector step_over_until_addrs; const bool abort_other_plans = false; const bool stop_other_threads = false; const bool check_inlines = true; const bool exact = false; SymbolContextList sc_list; const uint32_t num_matches = frame_sc.comp_unit->ResolveSymbolContext( step_file_spec, line, check_inlines, exact, eSymbolContextLineEntry, sc_list); if (num_matches > 0) { SymbolContext sc; for (uint32_t i = 0; i < num_matches; ++i) { if (sc_list.GetContextAtIndex(i, sc)) { addr_t step_addr = sc.line_entry.range.GetBaseAddress().GetLoadAddress(target); if (step_addr != LLDB_INVALID_ADDRESS) { if (fun_range.ContainsLoadAddress(step_addr, target)) step_over_until_addrs.push_back(step_addr); else all_in_function = false; } } } } if (step_over_until_addrs.empty()) { if (all_in_function) { step_file_spec.GetPath(path, sizeof(path)); sb_error.SetErrorStringWithFormat("No line entries for %s:%u", path, line); } else sb_error.SetErrorString("step until target not in current function"); } else { ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepUntil( abort_other_plans, &step_over_until_addrs[0], step_over_until_addrs.size(), stop_other_threads, frame_sp->GetFrameIndex())); sb_error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); } } else { sb_error.SetErrorString("this SBThread object is invalid"); } return sb_error; } SBError SBThread::StepUsingScriptedThreadPlan(const char *script_class_name) { return StepUsingScriptedThreadPlan(script_class_name, true); } SBError SBThread::StepUsingScriptedThreadPlan(const char *script_class_name, bool resume_immediately) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBError sb_error; std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (log) { log->Printf("SBThread(%p)::StepUsingScriptedThreadPlan: class name: %s", static_cast(exe_ctx.GetThreadPtr()), script_class_name); } if (!exe_ctx.HasThreadScope()) { sb_error.SetErrorString("this SBThread object is invalid"); return sb_error; } Thread *thread = exe_ctx.GetThreadPtr(); ThreadPlanSP thread_plan_sp = thread->QueueThreadPlanForStepScripted(false, script_class_name, false); if (!thread_plan_sp) { sb_error.SetErrorStringWithFormat( "Error queueing thread plan for class: %s", script_class_name); return sb_error; } if (!resume_immediately) { return sb_error; } if (thread_plan_sp) sb_error = ResumeNewPlan(exe_ctx, thread_plan_sp.get()); else { sb_error.SetErrorStringWithFormat( "Error resuming thread plan for class: %s.", script_class_name); if (log) log->Printf("SBThread(%p)::StepUsingScriptedThreadPlan: Error queuing " "thread plan for class: %s", static_cast(exe_ctx.GetThreadPtr()), script_class_name); } return sb_error; } SBError SBThread::JumpToLine(lldb::SBFileSpec &file_spec, uint32_t line) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBError sb_error; std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (log) log->Printf("SBThread(%p)::JumpToLine (file+line = %s:%u)", static_cast(exe_ctx.GetThreadPtr()), file_spec->GetPath().c_str(), line); if (!exe_ctx.HasThreadScope()) { sb_error.SetErrorString("this SBThread object is invalid"); return sb_error; } Thread *thread = exe_ctx.GetThreadPtr(); Status err = thread->JumpToLine(file_spec.get(), line, true); sb_error.SetError(err); return sb_error; } SBError SBThread::ReturnFromFrame(SBFrame &frame, SBValue &return_value) { SBError sb_error; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (log) log->Printf("SBThread(%p)::ReturnFromFrame (frame=%d)", static_cast(exe_ctx.GetThreadPtr()), frame.GetFrameID()); if (exe_ctx.HasThreadScope()) { Thread *thread = exe_ctx.GetThreadPtr(); sb_error.SetError( thread->ReturnFromFrame(frame.GetFrameSP(), return_value.GetSP())); } return sb_error; } SBError SBThread::UnwindInnermostExpression() { SBError sb_error; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (log) log->Printf("SBThread(%p)::UnwindExpressionEvaluation", static_cast(exe_ctx.GetThreadPtr())); if (exe_ctx.HasThreadScope()) { Thread *thread = exe_ctx.GetThreadPtr(); sb_error.SetError(thread->UnwindInnermostExpression()); if (sb_error.Success()) thread->SetSelectedFrameByIndex(0, false); } return sb_error; } bool SBThread::Suspend() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); bool result = false; if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { exe_ctx.GetThreadPtr()->SetResumeState(eStateSuspended); result = true; } else { if (log) log->Printf("SBThread(%p)::Suspend() => error: process is running", static_cast(exe_ctx.GetThreadPtr())); } } if (log) log->Printf("SBThread(%p)::Suspend() => %i", static_cast(exe_ctx.GetThreadPtr()), result); return result; } bool SBThread::Resume() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); bool result = false; if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { const bool override_suspend = true; exe_ctx.GetThreadPtr()->SetResumeState(eStateRunning, override_suspend); result = true; } else { if (log) log->Printf("SBThread(%p)::Resume() => error: process is running", static_cast(exe_ctx.GetThreadPtr())); } } if (log) log->Printf("SBThread(%p)::Resume() => %i", static_cast(exe_ctx.GetThreadPtr()), result); return result; } bool SBThread::IsSuspended() { std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (exe_ctx.HasThreadScope()) return exe_ctx.GetThreadPtr()->GetResumeState() == eStateSuspended; return false; } bool SBThread::IsStopped() { std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (exe_ctx.HasThreadScope()) return StateIsStoppedState(exe_ctx.GetThreadPtr()->GetState(), true); return false; } SBProcess SBThread::GetProcess() { SBProcess sb_process; std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (exe_ctx.HasThreadScope()) { // Have to go up to the target so we can get a shared pointer to our // process... sb_process.SetSP(exe_ctx.GetProcessSP()); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); if (log) { SBStream frame_desc_strm; sb_process.GetDescription(frame_desc_strm); log->Printf("SBThread(%p)::GetProcess () => SBProcess(%p): %s", static_cast(exe_ctx.GetThreadPtr()), static_cast(sb_process.GetSP().get()), frame_desc_strm.GetData()); } return sb_process; } uint32_t SBThread::GetNumFrames() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); uint32_t num_frames = 0; std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { num_frames = exe_ctx.GetThreadPtr()->GetStackFrameCount(); } else { if (log) log->Printf("SBThread(%p)::GetNumFrames() => error: process is running", static_cast(exe_ctx.GetThreadPtr())); } } if (log) log->Printf("SBThread(%p)::GetNumFrames () => %u", static_cast(exe_ctx.GetThreadPtr()), num_frames); return num_frames; } SBFrame SBThread::GetFrameAtIndex(uint32_t idx) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBFrame sb_frame; StackFrameSP frame_sp; std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { frame_sp = exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(idx); sb_frame.SetFrameSP(frame_sp); } else { if (log) log->Printf( "SBThread(%p)::GetFrameAtIndex() => error: process is running", static_cast(exe_ctx.GetThreadPtr())); } } if (log) { SBStream frame_desc_strm; sb_frame.GetDescription(frame_desc_strm); log->Printf("SBThread(%p)::GetFrameAtIndex (idx=%d) => SBFrame(%p): %s", static_cast(exe_ctx.GetThreadPtr()), idx, static_cast(frame_sp.get()), frame_desc_strm.GetData()); } return sb_frame; } lldb::SBFrame SBThread::GetSelectedFrame() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBFrame sb_frame; StackFrameSP frame_sp; std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { frame_sp = exe_ctx.GetThreadPtr()->GetSelectedFrame(); sb_frame.SetFrameSP(frame_sp); } else { if (log) log->Printf( "SBThread(%p)::GetSelectedFrame() => error: process is running", static_cast(exe_ctx.GetThreadPtr())); } } if (log) { SBStream frame_desc_strm; sb_frame.GetDescription(frame_desc_strm); log->Printf("SBThread(%p)::GetSelectedFrame () => SBFrame(%p): %s", static_cast(exe_ctx.GetThreadPtr()), static_cast(frame_sp.get()), frame_desc_strm.GetData()); } return sb_frame; } lldb::SBFrame SBThread::SetSelectedFrame(uint32_t idx) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); SBFrame sb_frame; StackFrameSP frame_sp; std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { Thread *thread = exe_ctx.GetThreadPtr(); frame_sp = thread->GetStackFrameAtIndex(idx); if (frame_sp) { thread->SetSelectedFrame(frame_sp.get()); sb_frame.SetFrameSP(frame_sp); } } else { if (log) log->Printf( "SBThread(%p)::SetSelectedFrame() => error: process is running", static_cast(exe_ctx.GetThreadPtr())); } } if (log) { SBStream frame_desc_strm; sb_frame.GetDescription(frame_desc_strm); log->Printf("SBThread(%p)::SetSelectedFrame (idx=%u) => SBFrame(%p): %s", static_cast(exe_ctx.GetThreadPtr()), idx, static_cast(frame_sp.get()), frame_desc_strm.GetData()); } return sb_frame; } bool SBThread::EventIsThreadEvent(const SBEvent &event) { return Thread::ThreadEventData::GetEventDataFromEvent(event.get()) != NULL; } SBFrame SBThread::GetStackFrameFromEvent(const SBEvent &event) { return Thread::ThreadEventData::GetStackFrameFromEvent(event.get()); } SBThread SBThread::GetThreadFromEvent(const SBEvent &event) { return Thread::ThreadEventData::GetThreadFromEvent(event.get()); } bool SBThread::operator==(const SBThread &rhs) const { return m_opaque_sp->GetThreadSP().get() == rhs.m_opaque_sp->GetThreadSP().get(); } bool SBThread::operator!=(const SBThread &rhs) const { return m_opaque_sp->GetThreadSP().get() != rhs.m_opaque_sp->GetThreadSP().get(); } bool SBThread::GetStatus(SBStream &status) const { Stream &strm = status.ref(); std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (exe_ctx.HasThreadScope()) { exe_ctx.GetThreadPtr()->GetStatus(strm, 0, 1, 1, true); } else strm.PutCString("No status"); return true; } bool SBThread::GetDescription(SBStream &description) const { return GetDescription(description, false); } bool SBThread::GetDescription(SBStream &description, bool stop_format) const { Stream &strm = description.ref(); std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); if (exe_ctx.HasThreadScope()) { exe_ctx.GetThreadPtr()->DumpUsingSettingsFormat(strm, LLDB_INVALID_THREAD_ID, stop_format); // strm.Printf("SBThread: tid = 0x%4.4" PRIx64, // exe_ctx.GetThreadPtr()->GetID()); } else strm.PutCString("No value"); return true; } SBThread SBThread::GetExtendedBacktraceThread(const char *type) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); std::unique_lock lock; ExecutionContext exe_ctx(m_opaque_sp.get(), lock); SBThread sb_origin_thread; if (exe_ctx.HasThreadScope()) { Process::StopLocker stop_locker; if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { ThreadSP real_thread(exe_ctx.GetThreadSP()); if (real_thread) { ConstString type_const(type); Process *process = exe_ctx.GetProcessPtr(); if (process) { SystemRuntime *runtime = process->GetSystemRuntime(); if (runtime) { ThreadSP new_thread_sp( runtime->GetExtendedBacktraceThread(real_thread, type_const)); if (new_thread_sp) { // Save this in the Process' ExtendedThreadList so a strong // pointer retains the // object. process->GetExtendedThreadList().AddThread(new_thread_sp); sb_origin_thread.SetThread(new_thread_sp); if (log) { const char *queue_name = new_thread_sp->GetQueueName(); if (queue_name == NULL) queue_name = ""; log->Printf("SBThread(%p)::GetExtendedBacktraceThread() => new " "extended Thread " "created (%p) with queue_id 0x%" PRIx64 " queue name '%s'", static_cast(exe_ctx.GetThreadPtr()), static_cast(new_thread_sp.get()), new_thread_sp->GetQueueID(), queue_name); } } } } } } else { if (log) log->Printf("SBThread(%p)::GetExtendedBacktraceThread() => error: " "process is running", static_cast(exe_ctx.GetThreadPtr())); } } if (log && sb_origin_thread.IsValid() == false) log->Printf("SBThread(%p)::GetExtendedBacktraceThread() is not returning a " "Valid thread", static_cast(exe_ctx.GetThreadPtr())); return sb_origin_thread; } uint32_t SBThread::GetExtendedBacktraceOriginatingIndexID() { ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); if (thread_sp) return thread_sp->GetExtendedBacktraceOriginatingIndexID(); return LLDB_INVALID_INDEX32; } bool SBThread::SafeToCallFunctions() { ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); if (thread_sp) return thread_sp->SafeToCallFunctions(); return true; } lldb_private::Thread *SBThread::operator->() { ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); if (thread_sp) return thread_sp.get(); else return NULL; } lldb_private::Thread *SBThread::get() { ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); if (thread_sp) return thread_sp.get(); else return NULL; } Index: vendor/lldb/dist/source/API/SBTrace.cpp =================================================================== --- vendor/lldb/dist/source/API/SBTrace.cpp (revision 319149) +++ vendor/lldb/dist/source/API/SBTrace.cpp (revision 319150) @@ -1,109 +1,109 @@ //===-- SBTrace.cpp ---------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -#include "lldb/Utility/Log.h" #include "lldb/Target/Process.h" +#include "lldb/Utility/Log.h" #include "lldb/API/SBTrace.h" #include "lldb/API/SBTraceOptions.h" using namespace lldb; using namespace lldb_private; class TraceImpl { public: lldb::user_id_t uid; }; lldb::ProcessSP SBTrace::GetSP() const { return m_opaque_wp.lock(); } size_t SBTrace::GetTraceData(SBError &error, void *buf, size_t size, size_t offset, lldb::tid_t thread_id) { - size_t bytes_read = 0; ProcessSP process_sp(GetSP()); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + llvm::MutableArrayRef buffer(static_cast(buf), size); error.Clear(); if (!process_sp) { error.SetErrorString("invalid process"); } else { - bytes_read = process_sp->GetData(GetTraceUID(), thread_id, error.ref(), buf, - size, offset); - LLDB_LOG(log, "SBTrace::bytes_read - %" PRIx64, bytes_read); + error.SetError( + process_sp->GetData(GetTraceUID(), thread_id, buffer, offset)); + LLDB_LOG(log, "SBTrace::bytes_read - {0}", buffer.size()); } - return bytes_read; + return buffer.size(); } size_t SBTrace::GetMetaData(SBError &error, void *buf, size_t size, size_t offset, lldb::tid_t thread_id) { - size_t bytes_read = 0; ProcessSP process_sp(GetSP()); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + llvm::MutableArrayRef buffer(static_cast(buf), size); error.Clear(); if (!process_sp) { error.SetErrorString("invalid process"); } else { - bytes_read = process_sp->GetMetaData(GetTraceUID(), thread_id, error.ref(), - buf, size, offset); - LLDB_LOG(log, "SBTrace::bytes_read - %" PRIx64, bytes_read); + error.SetError( + process_sp->GetMetaData(GetTraceUID(), thread_id, buffer, offset)); + LLDB_LOG(log, "SBTrace::bytes_read - {0}", buffer.size()); } - return bytes_read; + return buffer.size(); } void SBTrace::StopTrace(SBError &error, lldb::tid_t thread_id) { ProcessSP process_sp(GetSP()); error.Clear(); if (!process_sp) { error.SetErrorString("invalid process"); return; } - process_sp->StopTrace(GetTraceUID(), thread_id, error.ref()); + error.SetError(process_sp->StopTrace(GetTraceUID(), thread_id)); } void SBTrace::GetTraceConfig(SBTraceOptions &options, SBError &error) { ProcessSP process_sp(GetSP()); error.Clear(); if (!process_sp) { error.SetErrorString("invalid process"); } else { - process_sp->GetTraceConfig(GetTraceUID(), error.ref(), - options.m_traceoptions_sp); + error.SetError(process_sp->GetTraceConfig(GetTraceUID(), + *(options.m_traceoptions_sp))); } } lldb::user_id_t SBTrace::GetTraceUID() { if (m_trace_impl_sp) return m_trace_impl_sp->uid; return LLDB_INVALID_UID; } void SBTrace::SetTraceUID(lldb::user_id_t uid) { if (m_trace_impl_sp) m_trace_impl_sp->uid = uid; } SBTrace::SBTrace() { m_trace_impl_sp.reset(new TraceImpl); if (m_trace_impl_sp) m_trace_impl_sp->uid = LLDB_INVALID_UID; } void SBTrace::SetSP(const ProcessSP &process_sp) { m_opaque_wp = process_sp; } bool SBTrace::IsValid() { if (!m_trace_impl_sp) return false; if (!GetSP()) return false; return true; } Index: vendor/lldb/dist/source/Commands/CommandObjectThread.cpp =================================================================== --- vendor/lldb/dist/source/Commands/CommandObjectThread.cpp (revision 319149) +++ vendor/lldb/dist/source/Commands/CommandObjectThread.cpp (revision 319150) @@ -1,1988 +1,1988 @@ //===-- CommandObjectThread.cpp ---------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "CommandObjectThread.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Core/SourceManager.h" #include "lldb/Core/State.h" #include "lldb/Core/ValueObject.h" #include "lldb/Host/Host.h" #include "lldb/Host/OptionParser.h" #include "lldb/Host/StringConvert.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/Options.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/LineEntry.h" #include "lldb/Symbol/LineTable.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanStepInRange.h" #include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Target/ThreadPlanStepOut.h" #include "lldb/Target/ThreadPlanStepRange.h" #include "lldb/lldb-private.h" using namespace lldb; using namespace lldb_private; //------------------------------------------------------------------------- // CommandObjectThreadBacktrace //------------------------------------------------------------------------- class CommandObjectIterateOverThreads : public CommandObjectParsed { public: CommandObjectIterateOverThreads(CommandInterpreter &interpreter, const char *name, const char *help, const char *syntax, uint32_t flags) : CommandObjectParsed(interpreter, name, help, syntax, flags) {} ~CommandObjectIterateOverThreads() override = default; bool DoExecute(Args &command, CommandReturnObject &result) override { result.SetStatus(m_success_return); if (command.GetArgumentCount() == 0) { Thread *thread = m_exe_ctx.GetThreadPtr(); if (!HandleOneThread(thread->GetID(), result)) return false; return result.Succeeded(); } // Use tids instead of ThreadSPs to prevent deadlocking problems which // result from JIT-ing // code while iterating over the (locked) ThreadSP list. std::vector tids; if (command.GetArgumentCount() == 1 && ::strcmp(command.GetArgumentAtIndex(0), "all") == 0) { Process *process = m_exe_ctx.GetProcessPtr(); for (ThreadSP thread_sp : process->Threads()) tids.push_back(thread_sp->GetID()); } else { const size_t num_args = command.GetArgumentCount(); Process *process = m_exe_ctx.GetProcessPtr(); std::lock_guard guard( process->GetThreadList().GetMutex()); for (size_t i = 0; i < num_args; i++) { bool success; uint32_t thread_idx = StringConvert::ToUInt32( command.GetArgumentAtIndex(i), 0, 0, &success); if (!success) { result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n", command.GetArgumentAtIndex(i)); result.SetStatus(eReturnStatusFailed); return false; } ThreadSP thread = process->GetThreadList().FindThreadByIndexID(thread_idx); if (!thread) { result.AppendErrorWithFormat("no thread with index: \"%s\"\n", command.GetArgumentAtIndex(i)); result.SetStatus(eReturnStatusFailed); return false; } tids.push_back(thread->GetID()); } } uint32_t idx = 0; for (const lldb::tid_t &tid : tids) { if (idx != 0 && m_add_return) result.AppendMessage(""); if (!HandleOneThread(tid, result)) return false; ++idx; } return result.Succeeded(); } protected: // Override this to do whatever you need to do for one thread. // // If you return false, the iteration will stop, otherwise it will proceed. // The result is set to m_success_return (defaults to // eReturnStatusSuccessFinishResult) before the iteration, // so you only need to set the return status in HandleOneThread if you want to // indicate an error. // If m_add_return is true, a blank line will be inserted between each of the // listings (except the last one.) virtual bool HandleOneThread(lldb::tid_t, CommandReturnObject &result) = 0; ReturnStatus m_success_return = eReturnStatusSuccessFinishResult; bool m_add_return = true; }; //------------------------------------------------------------------------- // CommandObjectThreadBacktrace //------------------------------------------------------------------------- static OptionDefinition g_thread_backtrace_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCount, "How many frames to display (-1 for all)" }, { LLDB_OPT_SET_1, false, "start", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFrameIndex, "Frame in which to start the backtrace" }, { LLDB_OPT_SET_1, false, "extended", 'e', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "Show the extended backtrace, if available" } // clang-format on }; class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads { public: class CommandOptions : public Options { public: CommandOptions() : Options() { // Keep default values of all options in one place: OptionParsingStarting // () OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'c': { int32_t input_count = 0; if (option_arg.getAsInteger(0, m_count)) { m_count = UINT32_MAX; error.SetErrorStringWithFormat( "invalid integer value for option '%c'", short_option); } else if (input_count < 0) m_count = UINT32_MAX; } break; case 's': if (option_arg.getAsInteger(0, m_start)) error.SetErrorStringWithFormat( "invalid integer value for option '%c'", short_option); break; case 'e': { bool success; m_extended_backtrace = Args::StringToBoolean(option_arg, false, &success); if (!success) error.SetErrorStringWithFormat( "invalid boolean value for option '%c'", short_option); } break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_count = UINT32_MAX; m_start = 0; m_extended_backtrace = false; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_thread_backtrace_options); } // Instance variables to hold the values for command options. uint32_t m_count; uint32_t m_start; bool m_extended_backtrace; }; CommandObjectThreadBacktrace(CommandInterpreter &interpreter) : CommandObjectIterateOverThreads( interpreter, "thread backtrace", "Show thread call stacks. Defaults to the current thread, thread " "indexes can be specified as arguments. Use the thread-index " "\"all\" " "to see all threads.", nullptr, eCommandRequiresProcess | eCommandRequiresThread | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() {} ~CommandObjectThreadBacktrace() override = default; Options *GetOptions() override { return &m_options; } protected: void DoExtendedBacktrace(Thread *thread, CommandReturnObject &result) { SystemRuntime *runtime = thread->GetProcess()->GetSystemRuntime(); if (runtime) { Stream &strm = result.GetOutputStream(); const std::vector &types = runtime->GetExtendedBacktraceTypes(); for (auto type : types) { ThreadSP ext_thread_sp = runtime->GetExtendedBacktraceThread( thread->shared_from_this(), type); if (ext_thread_sp && ext_thread_sp->IsValid()) { const uint32_t num_frames_with_source = 0; const bool stop_format = false; if (ext_thread_sp->GetStatus(strm, m_options.m_start, m_options.m_count, num_frames_with_source, stop_format)) { DoExtendedBacktrace(ext_thread_sp.get(), result); } } } } } bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override { ThreadSP thread_sp = m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid); if (!thread_sp) { result.AppendErrorWithFormat( "thread disappeared while computing backtraces: 0x%" PRIx64 "\n", tid); result.SetStatus(eReturnStatusFailed); return false; } Thread *thread = thread_sp.get(); Stream &strm = result.GetOutputStream(); // Don't show source context when doing backtraces. const uint32_t num_frames_with_source = 0; const bool stop_format = true; if (!thread->GetStatus(strm, m_options.m_start, m_options.m_count, num_frames_with_source, stop_format)) { result.AppendErrorWithFormat( "error displaying backtrace for thread: \"0x%4.4x\"\n", thread->GetIndexID()); result.SetStatus(eReturnStatusFailed); return false; } if (m_options.m_extended_backtrace) { DoExtendedBacktrace(thread, result); } return true; } CommandOptions m_options; }; enum StepScope { eStepScopeSource, eStepScopeInstruction }; static OptionEnumValueElement g_tri_running_mode[] = { {eOnlyThisThread, "this-thread", "Run only this thread"}, {eAllThreads, "all-threads", "Run all threads"}, {eOnlyDuringStepping, "while-stepping", "Run only this thread while stepping"}, {0, nullptr, nullptr}}; static OptionEnumValueElement g_duo_running_mode[] = { {eOnlyThisThread, "this-thread", "Run only this thread"}, {eAllThreads, "all-threads", "Run all threads"}, {0, nullptr, nullptr}}; static OptionDefinition g_thread_step_scope_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "step-in-avoids-no-debug", 'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "A boolean value that sets whether stepping into functions will step over functions with no debug information." }, { LLDB_OPT_SET_1, false, "step-out-avoids-no-debug", 'A', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "A boolean value, if true stepping out of functions will continue to step out till it hits a function with debug information." }, { LLDB_OPT_SET_1, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 1, eArgTypeCount, "How many times to perform the stepping operation - currently only supported for step-inst and next-inst." }, { LLDB_OPT_SET_1, false, "end-linenumber", 'e', OptionParser::eRequiredArgument, nullptr, nullptr, 1, eArgTypeLineNum, "The line at which to stop stepping - defaults to the next line and only supported for step-in and step-over. You can also pass the string 'block' to step to the end of the current block. This is particularly useful in conjunction with --step-target to step through a complex calling sequence." }, { LLDB_OPT_SET_1, false, "run-mode", 'm', OptionParser::eRequiredArgument, nullptr, g_tri_running_mode, 0, eArgTypeRunMode, "Determine how to run other threads while stepping the current thread." }, { LLDB_OPT_SET_1, false, "step-over-regexp", 'r', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeRegularExpression, "A regular expression that defines function names to not to stop at when stepping in." }, { LLDB_OPT_SET_1, false, "step-in-target", 't', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFunctionName, "The name of the directly called function step in should stop at when stepping into." }, { LLDB_OPT_SET_2, false, "python-class", 'C', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePythonClass, "The name of the class that will manage this step - only supported for Scripted Step." } // clang-format on }; class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed { public: class CommandOptions : public Options { public: CommandOptions() : Options() { // Keep default values of all options in one place: OptionParsingStarting // () OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'a': { bool success; bool avoid_no_debug = Args::StringToBoolean(option_arg, true, &success); if (!success) error.SetErrorStringWithFormat( "invalid boolean value for option '%c'", short_option); else { m_step_in_avoid_no_debug = avoid_no_debug ? eLazyBoolYes : eLazyBoolNo; } } break; case 'A': { bool success; bool avoid_no_debug = Args::StringToBoolean(option_arg, true, &success); if (!success) error.SetErrorStringWithFormat( "invalid boolean value for option '%c'", short_option); else { m_step_out_avoid_no_debug = avoid_no_debug ? eLazyBoolYes : eLazyBoolNo; } } break; case 'c': if (option_arg.getAsInteger(0, m_step_count)) error.SetErrorStringWithFormat("invalid step count '%s'", option_arg.str().c_str()); break; case 'C': m_class_name.clear(); m_class_name.assign(option_arg); break; case 'm': { OptionEnumValueElement *enum_values = GetDefinitions()[option_idx].enum_values; m_run_mode = (lldb::RunMode)Args::StringToOptionEnum( option_arg, enum_values, eOnlyDuringStepping, error); } break; case 'e': if (option_arg == "block") { m_end_line_is_block_end = 1; break; } if (option_arg.getAsInteger(0, m_end_line)) error.SetErrorStringWithFormat("invalid end line number '%s'", option_arg.str().c_str()); break; case 'r': m_avoid_regexp.clear(); m_avoid_regexp.assign(option_arg); break; case 't': m_step_in_target.clear(); m_step_in_target.assign(option_arg); break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_step_in_avoid_no_debug = eLazyBoolCalculate; m_step_out_avoid_no_debug = eLazyBoolCalculate; m_run_mode = eOnlyDuringStepping; // Check if we are in Non-Stop mode TargetSP target_sp = execution_context ? execution_context->GetTargetSP() : TargetSP(); if (target_sp && target_sp->GetNonStopModeEnabled()) m_run_mode = eOnlyThisThread; m_avoid_regexp.clear(); m_step_in_target.clear(); m_class_name.clear(); m_step_count = 1; m_end_line = LLDB_INVALID_LINE_NUMBER; m_end_line_is_block_end = false; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_thread_step_scope_options); } // Instance variables to hold the values for command options. LazyBool m_step_in_avoid_no_debug; LazyBool m_step_out_avoid_no_debug; RunMode m_run_mode; std::string m_avoid_regexp; std::string m_step_in_target; std::string m_class_name; uint32_t m_step_count; uint32_t m_end_line; bool m_end_line_is_block_end; }; CommandObjectThreadStepWithTypeAndScope(CommandInterpreter &interpreter, const char *name, const char *help, const char *syntax, StepType step_type, StepScope step_scope) : CommandObjectParsed(interpreter, name, help, syntax, eCommandRequiresProcess | eCommandRequiresThread | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_step_type(step_type), m_step_scope(step_scope), m_options() { CommandArgumentEntry arg; CommandArgumentData thread_id_arg; // Define the first (and only) variant of this arg. thread_id_arg.arg_type = eArgTypeThreadID; thread_id_arg.arg_repetition = eArgRepeatOptional; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(thread_id_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectThreadStepWithTypeAndScope() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Process *process = m_exe_ctx.GetProcessPtr(); bool synchronous_execution = m_interpreter.GetSynchronous(); const uint32_t num_threads = process->GetThreadList().GetSize(); Thread *thread = nullptr; if (command.GetArgumentCount() == 0) { thread = GetDefaultThread(); if (thread == nullptr) { result.AppendError("no selected thread in process"); result.SetStatus(eReturnStatusFailed); return false; } } else { const char *thread_idx_cstr = command.GetArgumentAtIndex(0); uint32_t step_thread_idx = StringConvert::ToUInt32(thread_idx_cstr, LLDB_INVALID_INDEX32); if (step_thread_idx == LLDB_INVALID_INDEX32) { result.AppendErrorWithFormat("invalid thread index '%s'.\n", thread_idx_cstr); result.SetStatus(eReturnStatusFailed); return false; } thread = process->GetThreadList().FindThreadByIndexID(step_thread_idx).get(); if (thread == nullptr) { result.AppendErrorWithFormat( "Thread index %u is out of range (valid values are 0 - %u).\n", step_thread_idx, num_threads); result.SetStatus(eReturnStatusFailed); return false; } } if (m_step_type == eStepTypeScripted) { if (m_options.m_class_name.empty()) { result.AppendErrorWithFormat("empty class name for scripted step."); result.SetStatus(eReturnStatusFailed); return false; } else if (!m_interpreter.GetScriptInterpreter()->CheckObjectExists( m_options.m_class_name.c_str())) { result.AppendErrorWithFormat( "class for scripted step: \"%s\" does not exist.", m_options.m_class_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } } if (m_options.m_end_line != LLDB_INVALID_LINE_NUMBER && m_step_type != eStepTypeInto) { result.AppendErrorWithFormat( "end line option is only valid for step into"); result.SetStatus(eReturnStatusFailed); return false; } const bool abort_other_plans = false; const lldb::RunMode stop_other_threads = m_options.m_run_mode; // This is a bit unfortunate, but not all the commands in this command // object support // only while stepping, so I use the bool for them. bool bool_stop_other_threads; if (m_options.m_run_mode == eAllThreads) bool_stop_other_threads = false; else if (m_options.m_run_mode == eOnlyDuringStepping) bool_stop_other_threads = (m_step_type != eStepTypeOut && m_step_type != eStepTypeScripted); else bool_stop_other_threads = true; ThreadPlanSP new_plan_sp; if (m_step_type == eStepTypeInto) { StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); assert(frame != nullptr); if (frame->HasDebugInformation()) { AddressRange range; SymbolContext sc = frame->GetSymbolContext(eSymbolContextEverything); if (m_options.m_end_line != LLDB_INVALID_LINE_NUMBER) { Status error; if (!sc.GetAddressRangeFromHereToEndLine(m_options.m_end_line, range, error)) { result.AppendErrorWithFormat("invalid end-line option: %s.", error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } } else if (m_options.m_end_line_is_block_end) { Status error; Block *block = frame->GetSymbolContext(eSymbolContextBlock).block; if (!block) { result.AppendErrorWithFormat("Could not find the current block."); result.SetStatus(eReturnStatusFailed); return false; } AddressRange block_range; Address pc_address = frame->GetFrameCodeAddress(); block->GetRangeContainingAddress(pc_address, block_range); if (!block_range.GetBaseAddress().IsValid()) { result.AppendErrorWithFormat( "Could not find the current block address."); result.SetStatus(eReturnStatusFailed); return false; } lldb::addr_t pc_offset_in_block = pc_address.GetFileAddress() - block_range.GetBaseAddress().GetFileAddress(); lldb::addr_t range_length = block_range.GetByteSize() - pc_offset_in_block; range = AddressRange(pc_address, range_length); } else { range = sc.line_entry.range; } new_plan_sp = thread->QueueThreadPlanForStepInRange( abort_other_plans, range, frame->GetSymbolContext(eSymbolContextEverything), m_options.m_step_in_target.c_str(), stop_other_threads, m_options.m_step_in_avoid_no_debug, m_options.m_step_out_avoid_no_debug); if (new_plan_sp && !m_options.m_avoid_regexp.empty()) { ThreadPlanStepInRange *step_in_range_plan = static_cast(new_plan_sp.get()); step_in_range_plan->SetAvoidRegexp(m_options.m_avoid_regexp.c_str()); } } else new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction( false, abort_other_plans, bool_stop_other_threads); } else if (m_step_type == eStepTypeOver) { StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); if (frame->HasDebugInformation()) new_plan_sp = thread->QueueThreadPlanForStepOverRange( abort_other_plans, frame->GetSymbolContext(eSymbolContextEverything).line_entry, frame->GetSymbolContext(eSymbolContextEverything), stop_other_threads, m_options.m_step_out_avoid_no_debug); else new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction( true, abort_other_plans, bool_stop_other_threads); } else if (m_step_type == eStepTypeTrace) { new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction( false, abort_other_plans, bool_stop_other_threads); } else if (m_step_type == eStepTypeTraceOver) { new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction( true, abort_other_plans, bool_stop_other_threads); } else if (m_step_type == eStepTypeOut) { new_plan_sp = thread->QueueThreadPlanForStepOut( abort_other_plans, nullptr, false, bool_stop_other_threads, eVoteYes, eVoteNoOpinion, thread->GetSelectedFrameIndex(), m_options.m_step_out_avoid_no_debug); } else if (m_step_type == eStepTypeScripted) { new_plan_sp = thread->QueueThreadPlanForStepScripted( abort_other_plans, m_options.m_class_name.c_str(), bool_stop_other_threads); } else { result.AppendError("step type is not supported"); result.SetStatus(eReturnStatusFailed); return false; } // If we got a new plan, then set it to be a master plan (User level Plans // should be master plans // so that they can be interruptible). Then resume the process. if (new_plan_sp) { new_plan_sp->SetIsMasterPlan(true); new_plan_sp->SetOkayToDiscard(false); if (m_options.m_step_count > 1) { - if (new_plan_sp->SetIterationCount(m_options.m_step_count)) { + if (!new_plan_sp->SetIterationCount(m_options.m_step_count)) { result.AppendWarning( "step operation does not support iteration count."); } } process->GetThreadList().SetSelectedThreadByID(thread->GetID()); const uint32_t iohandler_id = process->GetIOHandlerID(); StreamString stream; Status error; if (synchronous_execution) error = process->ResumeSynchronous(&stream); else error = process->Resume(); // There is a race condition where this thread will return up the call // stack to the main command handler // and show an (lldb) prompt before HandlePrivateEvent (from // PrivateStateThread) has // a chance to call PushProcessIOHandler(). process->SyncIOHandler(iohandler_id, 2000); if (synchronous_execution) { // If any state changed events had anything to say, add that to the // result if (stream.GetSize() > 0) result.AppendMessage(stream.GetString()); process->GetThreadList().SetSelectedThreadByID(thread->GetID()); result.SetDidChangeProcessState(true); result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.SetStatus(eReturnStatusSuccessContinuingNoResult); } } else { result.AppendError("Couldn't find thread plan to implement step type."); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } protected: StepType m_step_type; StepScope m_step_scope; CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectThreadContinue //------------------------------------------------------------------------- class CommandObjectThreadContinue : public CommandObjectParsed { public: CommandObjectThreadContinue(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "thread continue", "Continue execution of the current target process. One " "or more threads may be specified, by default all " "threads continue.", nullptr, eCommandRequiresThread | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) { CommandArgumentEntry arg; CommandArgumentData thread_idx_arg; // Define the first (and only) variant of this arg. thread_idx_arg.arg_type = eArgTypeThreadIndex; thread_idx_arg.arg_repetition = eArgRepeatPlus; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(thread_idx_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectThreadContinue() override = default; bool DoExecute(Args &command, CommandReturnObject &result) override { bool synchronous_execution = m_interpreter.GetSynchronous(); if (!m_interpreter.GetDebugger().GetSelectedTarget()) { result.AppendError("invalid target, create a debug target using the " "'target create' command"); result.SetStatus(eReturnStatusFailed); return false; } Process *process = m_exe_ctx.GetProcessPtr(); if (process == nullptr) { result.AppendError("no process exists. Cannot continue"); result.SetStatus(eReturnStatusFailed); return false; } StateType state = process->GetState(); if ((state == eStateCrashed) || (state == eStateStopped) || (state == eStateSuspended)) { const size_t argc = command.GetArgumentCount(); if (argc > 0) { // These two lines appear at the beginning of both blocks in // this if..else, but that is because we need to release the // lock before calling process->Resume below. std::lock_guard guard( process->GetThreadList().GetMutex()); const uint32_t num_threads = process->GetThreadList().GetSize(); std::vector resume_threads; for (auto &entry : command.entries()) { uint32_t thread_idx; if (entry.ref.getAsInteger(0, thread_idx)) { result.AppendErrorWithFormat( "invalid thread index argument: \"%s\".\n", entry.c_str()); result.SetStatus(eReturnStatusFailed); return false; } Thread *thread = process->GetThreadList().FindThreadByIndexID(thread_idx).get(); if (thread) { resume_threads.push_back(thread); } else { result.AppendErrorWithFormat("invalid thread index %u.\n", thread_idx); result.SetStatus(eReturnStatusFailed); return false; } } if (resume_threads.empty()) { result.AppendError("no valid thread indexes were specified"); result.SetStatus(eReturnStatusFailed); return false; } else { if (resume_threads.size() == 1) result.AppendMessageWithFormat("Resuming thread: "); else result.AppendMessageWithFormat("Resuming threads: "); for (uint32_t idx = 0; idx < num_threads; ++idx) { Thread *thread = process->GetThreadList().GetThreadAtIndex(idx).get(); std::vector::iterator this_thread_pos = find(resume_threads.begin(), resume_threads.end(), thread); if (this_thread_pos != resume_threads.end()) { resume_threads.erase(this_thread_pos); if (!resume_threads.empty()) result.AppendMessageWithFormat("%u, ", thread->GetIndexID()); else result.AppendMessageWithFormat("%u ", thread->GetIndexID()); const bool override_suspend = true; thread->SetResumeState(eStateRunning, override_suspend); } else { thread->SetResumeState(eStateSuspended); } } result.AppendMessageWithFormat("in process %" PRIu64 "\n", process->GetID()); } } else { // These two lines appear at the beginning of both blocks in // this if..else, but that is because we need to release the // lock before calling process->Resume below. std::lock_guard guard( process->GetThreadList().GetMutex()); const uint32_t num_threads = process->GetThreadList().GetSize(); Thread *current_thread = GetDefaultThread(); if (current_thread == nullptr) { result.AppendError("the process doesn't have a current thread"); result.SetStatus(eReturnStatusFailed); return false; } // Set the actions that the threads should each take when resuming for (uint32_t idx = 0; idx < num_threads; ++idx) { Thread *thread = process->GetThreadList().GetThreadAtIndex(idx).get(); if (thread == current_thread) { result.AppendMessageWithFormat("Resuming thread 0x%4.4" PRIx64 " in process %" PRIu64 "\n", thread->GetID(), process->GetID()); const bool override_suspend = true; thread->SetResumeState(eStateRunning, override_suspend); } else { thread->SetResumeState(eStateSuspended); } } } StreamString stream; Status error; if (synchronous_execution) error = process->ResumeSynchronous(&stream); else error = process->Resume(); // We should not be holding the thread list lock when we do this. if (error.Success()) { result.AppendMessageWithFormat("Process %" PRIu64 " resuming\n", process->GetID()); if (synchronous_execution) { // If any state changed events had anything to say, add that to the // result if (stream.GetSize() > 0) result.AppendMessage(stream.GetString()); result.SetDidChangeProcessState(true); result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.SetStatus(eReturnStatusSuccessContinuingNoResult); } } else { result.AppendErrorWithFormat("Failed to resume process: %s\n", error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendErrorWithFormat( "Process cannot be continued from its current state (%s).\n", StateAsCString(state)); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectThreadUntil //------------------------------------------------------------------------- static OptionDefinition g_thread_until_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "frame", 'f', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFrameIndex, "Frame index for until operation - defaults to 0" }, { LLDB_OPT_SET_1, false, "thread", 't', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadIndex, "Thread index for the thread for until operation" }, { LLDB_OPT_SET_1, false, "run-mode",'m', OptionParser::eRequiredArgument, nullptr, g_duo_running_mode, 0, eArgTypeRunMode, "Determine how to run other threads while stepping this one" }, { LLDB_OPT_SET_1, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Run until we reach the specified address, or leave the function - can be specified multiple times." } // clang-format on }; class CommandObjectThreadUntil : public CommandObjectParsed { public: class CommandOptions : public Options { public: uint32_t m_thread_idx; uint32_t m_frame_idx; CommandOptions() : Options(), m_thread_idx(LLDB_INVALID_THREAD_ID), m_frame_idx(LLDB_INVALID_FRAME_ID) { // Keep default values of all options in one place: OptionParsingStarting // () OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'a': { lldb::addr_t tmp_addr = Args::StringToAddress( execution_context, option_arg, LLDB_INVALID_ADDRESS, &error); if (error.Success()) m_until_addrs.push_back(tmp_addr); } break; case 't': if (option_arg.getAsInteger(0, m_thread_idx)) { m_thread_idx = LLDB_INVALID_INDEX32; error.SetErrorStringWithFormat("invalid thread index '%s'", option_arg.str().c_str()); } break; case 'f': if (option_arg.getAsInteger(0, m_frame_idx)) { m_frame_idx = LLDB_INVALID_FRAME_ID; error.SetErrorStringWithFormat("invalid frame index '%s'", option_arg.str().c_str()); } break; case 'm': { OptionEnumValueElement *enum_values = GetDefinitions()[option_idx].enum_values; lldb::RunMode run_mode = (lldb::RunMode)Args::StringToOptionEnum( option_arg, enum_values, eOnlyDuringStepping, error); if (error.Success()) { if (run_mode == eAllThreads) m_stop_others = false; else m_stop_others = true; } } break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_thread_idx = LLDB_INVALID_THREAD_ID; m_frame_idx = 0; m_stop_others = false; m_until_addrs.clear(); } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_thread_until_options); } uint32_t m_step_thread_idx; bool m_stop_others; std::vector m_until_addrs; // Instance variables to hold the values for command options. }; CommandObjectThreadUntil(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "thread until", "Continue until a line number or address is reached by the " "current or specified thread. Stops when returning from " "the current function as a safety measure. " "The target line number(s) are given as arguments, and if more than one" " is provided, stepping will stop when the first one is hit.", nullptr, eCommandRequiresThread | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() { CommandArgumentEntry arg; CommandArgumentData line_num_arg; // Define the first (and only) variant of this arg. line_num_arg.arg_type = eArgTypeLineNum; line_num_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(line_num_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectThreadUntil() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { bool synchronous_execution = m_interpreter.GetSynchronous(); Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target == nullptr) { result.AppendError("invalid target, create a debug target using the " "'target create' command"); result.SetStatus(eReturnStatusFailed); return false; } Process *process = m_exe_ctx.GetProcessPtr(); if (process == nullptr) { result.AppendError("need a valid process to step"); result.SetStatus(eReturnStatusFailed); } else { Thread *thread = nullptr; std::vector line_numbers; if (command.GetArgumentCount() >= 1) { size_t num_args = command.GetArgumentCount(); for (size_t i = 0; i < num_args; i++) { uint32_t line_number; line_number = StringConvert::ToUInt32(command.GetArgumentAtIndex(i), UINT32_MAX); if (line_number == UINT32_MAX) { result.AppendErrorWithFormat("invalid line number: '%s'.\n", command.GetArgumentAtIndex(i)); result.SetStatus(eReturnStatusFailed); return false; } else line_numbers.push_back(line_number); } } else if (m_options.m_until_addrs.empty()) { result.AppendErrorWithFormat("No line number or address provided:\n%s", GetSyntax().str().c_str()); result.SetStatus(eReturnStatusFailed); return false; } if (m_options.m_thread_idx == LLDB_INVALID_THREAD_ID) { thread = GetDefaultThread(); } else { thread = process->GetThreadList() .FindThreadByIndexID(m_options.m_thread_idx) .get(); } if (thread == nullptr) { const uint32_t num_threads = process->GetThreadList().GetSize(); result.AppendErrorWithFormat( "Thread index %u is out of range (valid values are 0 - %u).\n", m_options.m_thread_idx, num_threads); result.SetStatus(eReturnStatusFailed); return false; } const bool abort_other_plans = false; StackFrame *frame = thread->GetStackFrameAtIndex(m_options.m_frame_idx).get(); if (frame == nullptr) { result.AppendErrorWithFormat( "Frame index %u is out of range for thread %u.\n", m_options.m_frame_idx, m_options.m_thread_idx); result.SetStatus(eReturnStatusFailed); return false; } ThreadPlanSP new_plan_sp; if (frame->HasDebugInformation()) { // Finally we got here... Translate the given line number to a bunch of // addresses: SymbolContext sc(frame->GetSymbolContext(eSymbolContextCompUnit)); LineTable *line_table = nullptr; if (sc.comp_unit) line_table = sc.comp_unit->GetLineTable(); if (line_table == nullptr) { result.AppendErrorWithFormat("Failed to resolve the line table for " "frame %u of thread index %u.\n", m_options.m_frame_idx, m_options.m_thread_idx); result.SetStatus(eReturnStatusFailed); return false; } LineEntry function_start; uint32_t index_ptr = 0, end_ptr; std::vector address_list; // Find the beginning & end index of the AddressRange fun_addr_range = sc.function->GetAddressRange(); Address fun_start_addr = fun_addr_range.GetBaseAddress(); line_table->FindLineEntryByAddress(fun_start_addr, function_start, &index_ptr); Address fun_end_addr(fun_start_addr.GetSection(), fun_start_addr.GetOffset() + fun_addr_range.GetByteSize()); bool all_in_function = true; line_table->FindLineEntryByAddress(fun_end_addr, function_start, &end_ptr); for (uint32_t line_number : line_numbers) { uint32_t start_idx_ptr = index_ptr; while (start_idx_ptr <= end_ptr) { LineEntry line_entry; const bool exact = false; start_idx_ptr = sc.comp_unit->FindLineEntry( start_idx_ptr, line_number, sc.comp_unit, exact, &line_entry); if (start_idx_ptr == UINT32_MAX) break; addr_t address = line_entry.range.GetBaseAddress().GetLoadAddress(target); if (address != LLDB_INVALID_ADDRESS) { if (fun_addr_range.ContainsLoadAddress(address, target)) address_list.push_back(address); else all_in_function = false; } start_idx_ptr++; } } for (lldb::addr_t address : m_options.m_until_addrs) { if (fun_addr_range.ContainsLoadAddress(address, target)) address_list.push_back(address); else all_in_function = false; } if (address_list.empty()) { if (all_in_function) result.AppendErrorWithFormat( "No line entries matching until target.\n"); else result.AppendErrorWithFormat( "Until target outside of the current function.\n"); result.SetStatus(eReturnStatusFailed); return false; } new_plan_sp = thread->QueueThreadPlanForStepUntil( abort_other_plans, &address_list.front(), address_list.size(), m_options.m_stop_others, m_options.m_frame_idx); // User level plans should be master plans so they can be interrupted // (e.g. by hitting a breakpoint) // and other plans executed by the user (stepping around the breakpoint) // and then a "continue" // will resume the original plan. new_plan_sp->SetIsMasterPlan(true); new_plan_sp->SetOkayToDiscard(false); } else { result.AppendErrorWithFormat( "Frame index %u of thread %u has no debug information.\n", m_options.m_frame_idx, m_options.m_thread_idx); result.SetStatus(eReturnStatusFailed); return false; } process->GetThreadList().SetSelectedThreadByID(m_options.m_thread_idx); StreamString stream; Status error; if (synchronous_execution) error = process->ResumeSynchronous(&stream); else error = process->Resume(); if (error.Success()) { result.AppendMessageWithFormat("Process %" PRIu64 " resuming\n", process->GetID()); if (synchronous_execution) { // If any state changed events had anything to say, add that to the // result if (stream.GetSize() > 0) result.AppendMessage(stream.GetString()); result.SetDidChangeProcessState(true); result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.SetStatus(eReturnStatusSuccessContinuingNoResult); } } else { result.AppendErrorWithFormat("Failed to resume process: %s.\n", error.AsCString()); result.SetStatus(eReturnStatusFailed); } } return result.Succeeded(); } CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectThreadSelect //------------------------------------------------------------------------- class CommandObjectThreadSelect : public CommandObjectParsed { public: CommandObjectThreadSelect(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "thread select", "Change the currently selected thread.", nullptr, eCommandRequiresProcess | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) { CommandArgumentEntry arg; CommandArgumentData thread_idx_arg; // Define the first (and only) variant of this arg. thread_idx_arg.arg_type = eArgTypeThreadIndex; thread_idx_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(thread_idx_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectThreadSelect() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Process *process = m_exe_ctx.GetProcessPtr(); if (process == nullptr) { result.AppendError("no process"); result.SetStatus(eReturnStatusFailed); return false; } else if (command.GetArgumentCount() != 1) { result.AppendErrorWithFormat( "'%s' takes exactly one thread index argument:\nUsage: %s\n", m_cmd_name.c_str(), m_cmd_syntax.c_str()); result.SetStatus(eReturnStatusFailed); return false; } uint32_t index_id = StringConvert::ToUInt32(command.GetArgumentAtIndex(0), 0, 0); Thread *new_thread = process->GetThreadList().FindThreadByIndexID(index_id).get(); if (new_thread == nullptr) { result.AppendErrorWithFormat("invalid thread #%s.\n", command.GetArgumentAtIndex(0)); result.SetStatus(eReturnStatusFailed); return false; } process->GetThreadList().SetSelectedThreadByID(new_thread->GetID(), true); result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectThreadList //------------------------------------------------------------------------- class CommandObjectThreadList : public CommandObjectParsed { public: CommandObjectThreadList(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "thread list", "Show a summary of each thread in the current target process.", "thread list", eCommandRequiresProcess | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {} ~CommandObjectThreadList() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Stream &strm = result.GetOutputStream(); result.SetStatus(eReturnStatusSuccessFinishNoResult); Process *process = m_exe_ctx.GetProcessPtr(); const bool only_threads_with_stop_reason = false; const uint32_t start_frame = 0; const uint32_t num_frames = 0; const uint32_t num_frames_with_source = 0; process->GetStatus(strm); process->GetThreadStatus(strm, only_threads_with_stop_reason, start_frame, num_frames, num_frames_with_source, false); return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectThreadInfo //------------------------------------------------------------------------- static OptionDefinition g_thread_info_options[] = { // clang-format off { LLDB_OPT_SET_ALL, false, "json", 'j', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Display the thread info in JSON format." }, { LLDB_OPT_SET_ALL, false, "stop-info", 's', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Display the extended stop info in JSON format." } // clang-format on }; class CommandObjectThreadInfo : public CommandObjectIterateOverThreads { public: class CommandOptions : public Options { public: CommandOptions() : Options() { OptionParsingStarting(nullptr); } ~CommandOptions() override = default; void OptionParsingStarting(ExecutionContext *execution_context) override { m_json_thread = false; m_json_stopinfo = false; } Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { const int short_option = m_getopt_table[option_idx].val; Status error; switch (short_option) { case 'j': m_json_thread = true; break; case 's': m_json_stopinfo = true; break; default: return Status("invalid short option character '%c'", short_option); } return error; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_thread_info_options); } bool m_json_thread; bool m_json_stopinfo; }; CommandObjectThreadInfo(CommandInterpreter &interpreter) : CommandObjectIterateOverThreads( interpreter, "thread info", "Show an extended summary of one or " "more threads. Defaults to the " "current thread.", "thread info", eCommandRequiresProcess | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() { m_add_return = false; } ~CommandObjectThreadInfo() override = default; Options *GetOptions() override { return &m_options; } bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override { ThreadSP thread_sp = m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid); if (!thread_sp) { result.AppendErrorWithFormat("thread no longer exists: 0x%" PRIx64 "\n", tid); result.SetStatus(eReturnStatusFailed); return false; } Thread *thread = thread_sp.get(); Stream &strm = result.GetOutputStream(); if (!thread->GetDescription(strm, eDescriptionLevelFull, m_options.m_json_thread, m_options.m_json_stopinfo)) { result.AppendErrorWithFormat("error displaying info for thread: \"%d\"\n", thread->GetIndexID()); result.SetStatus(eReturnStatusFailed); return false; } return true; } CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectThreadReturn //------------------------------------------------------------------------- static OptionDefinition g_thread_return_options[] = { // clang-format off { LLDB_OPT_SET_ALL, false, "from-expression", 'x', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Return from the innermost expression evaluation." } // clang-format on }; class CommandObjectThreadReturn : public CommandObjectRaw { public: class CommandOptions : public Options { public: CommandOptions() : Options(), m_from_expression(false) { // Keep default values of all options in one place: OptionParsingStarting // () OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'x': { bool success; bool tmp_value = Args::StringToBoolean(option_arg, false, &success); if (success) m_from_expression = tmp_value; else { error.SetErrorStringWithFormat( "invalid boolean value '%s' for 'x' option", option_arg.str().c_str()); } } break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_from_expression = false; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_thread_return_options); } bool m_from_expression; // Instance variables to hold the values for command options. }; CommandObjectThreadReturn(CommandInterpreter &interpreter) : CommandObjectRaw(interpreter, "thread return", "Prematurely return from a stack frame, " "short-circuiting execution of newer frames " "and optionally yielding a specified value. Defaults " "to the exiting the current stack " "frame.", "thread return", eCommandRequiresFrame | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() { CommandArgumentEntry arg; CommandArgumentData expression_arg; // Define the first (and only) variant of this arg. expression_arg.arg_type = eArgTypeExpression; expression_arg.arg_repetition = eArgRepeatOptional; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(expression_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectThreadReturn() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(const char *command, CommandReturnObject &result) override { // I am going to handle this by hand, because I don't want you to have to // say: // "thread return -- -5". if (command[0] == '-' && command[1] == 'x') { if (command && command[2] != '\0') result.AppendWarning("Return values ignored when returning from user " "called expressions"); Thread *thread = m_exe_ctx.GetThreadPtr(); Status error; error = thread->UnwindInnermostExpression(); if (!error.Success()) { result.AppendErrorWithFormat("Unwinding expression failed - %s.", error.AsCString()); result.SetStatus(eReturnStatusFailed); } else { bool success = thread->SetSelectedFrameByIndexNoisily(0, result.GetOutputStream()); if (success) { m_exe_ctx.SetFrameSP(thread->GetSelectedFrame()); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat( "Could not select 0th frame after unwinding expression."); result.SetStatus(eReturnStatusFailed); } } return result.Succeeded(); } ValueObjectSP return_valobj_sp; StackFrameSP frame_sp = m_exe_ctx.GetFrameSP(); uint32_t frame_idx = frame_sp->GetFrameIndex(); if (frame_sp->IsInlined()) { result.AppendError("Don't know how to return from inlined frames."); result.SetStatus(eReturnStatusFailed); return false; } if (command && command[0] != '\0') { Target *target = m_exe_ctx.GetTargetPtr(); EvaluateExpressionOptions options; options.SetUnwindOnError(true); options.SetUseDynamic(eNoDynamicValues); ExpressionResults exe_results = eExpressionSetupError; exe_results = target->EvaluateExpression(command, frame_sp.get(), return_valobj_sp, options); if (exe_results != eExpressionCompleted) { if (return_valobj_sp) result.AppendErrorWithFormat( "Error evaluating result expression: %s", return_valobj_sp->GetError().AsCString()); else result.AppendErrorWithFormat( "Unknown error evaluating result expression."); result.SetStatus(eReturnStatusFailed); return false; } } Status error; ThreadSP thread_sp = m_exe_ctx.GetThreadSP(); const bool broadcast = true; error = thread_sp->ReturnFromFrame(frame_sp, return_valobj_sp, broadcast); if (!error.Success()) { result.AppendErrorWithFormat( "Error returning from frame %d of thread %d: %s.", frame_idx, thread_sp->GetIndexID(), error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } result.SetStatus(eReturnStatusSuccessFinishResult); return true; } CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectThreadJump //------------------------------------------------------------------------- static OptionDefinition g_thread_jump_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "Specifies the source file to jump to." }, { LLDB_OPT_SET_1, true, "line", 'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLineNum, "Specifies the line number to jump to." }, { LLDB_OPT_SET_2, true, "by", 'b', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeOffset, "Jumps by a relative line offset from the current line." }, { LLDB_OPT_SET_3, true, "address", 'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Jumps to a specific address." }, { LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "force", 'r', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Allows the PC to leave the current function." } // clang-format on }; class CommandObjectThreadJump : public CommandObjectParsed { public: class CommandOptions : public Options { public: CommandOptions() : Options() { OptionParsingStarting(nullptr); } ~CommandOptions() override = default; void OptionParsingStarting(ExecutionContext *execution_context) override { m_filenames.Clear(); m_line_num = 0; m_line_offset = 0; m_load_addr = LLDB_INVALID_ADDRESS; m_force = false; } Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { const int short_option = m_getopt_table[option_idx].val; Status error; switch (short_option) { case 'f': m_filenames.AppendIfUnique(FileSpec(option_arg, false)); if (m_filenames.GetSize() > 1) return Status("only one source file expected."); break; case 'l': if (option_arg.getAsInteger(0, m_line_num)) return Status("invalid line number: '%s'.", option_arg.str().c_str()); break; case 'b': if (option_arg.getAsInteger(0, m_line_offset)) return Status("invalid line offset: '%s'.", option_arg.str().c_str()); break; case 'a': m_load_addr = Args::StringToAddress(execution_context, option_arg, LLDB_INVALID_ADDRESS, &error); break; case 'r': m_force = true; break; default: return Status("invalid short option character '%c'", short_option); } return error; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_thread_jump_options); } FileSpecList m_filenames; uint32_t m_line_num; int32_t m_line_offset; lldb::addr_t m_load_addr; bool m_force; }; CommandObjectThreadJump(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "thread jump", "Sets the program counter to a new address.", "thread jump", eCommandRequiresFrame | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() {} ~CommandObjectThreadJump() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(Args &args, CommandReturnObject &result) override { RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext(); StackFrame *frame = m_exe_ctx.GetFramePtr(); Thread *thread = m_exe_ctx.GetThreadPtr(); Target *target = m_exe_ctx.GetTargetPtr(); const SymbolContext &sym_ctx = frame->GetSymbolContext(eSymbolContextLineEntry); if (m_options.m_load_addr != LLDB_INVALID_ADDRESS) { // Use this address directly. Address dest = Address(m_options.m_load_addr); lldb::addr_t callAddr = dest.GetCallableLoadAddress(target); if (callAddr == LLDB_INVALID_ADDRESS) { result.AppendErrorWithFormat("Invalid destination address."); result.SetStatus(eReturnStatusFailed); return false; } if (!reg_ctx->SetPC(callAddr)) { result.AppendErrorWithFormat("Error changing PC value for thread %d.", thread->GetIndexID()); result.SetStatus(eReturnStatusFailed); return false; } } else { // Pick either the absolute line, or work out a relative one. int32_t line = (int32_t)m_options.m_line_num; if (line == 0) line = sym_ctx.line_entry.line + m_options.m_line_offset; // Try the current file, but override if asked. FileSpec file = sym_ctx.line_entry.file; if (m_options.m_filenames.GetSize() == 1) file = m_options.m_filenames.GetFileSpecAtIndex(0); if (!file) { result.AppendErrorWithFormat( "No source file available for the current location."); result.SetStatus(eReturnStatusFailed); return false; } std::string warnings; Status err = thread->JumpToLine(file, line, m_options.m_force, &warnings); if (err.Fail()) { result.SetError(err); return false; } if (!warnings.empty()) result.AppendWarning(warnings.c_str()); } result.SetStatus(eReturnStatusSuccessFinishResult); return true; } CommandOptions m_options; }; //------------------------------------------------------------------------- // Next are the subcommands of CommandObjectMultiwordThreadPlan //------------------------------------------------------------------------- //------------------------------------------------------------------------- // CommandObjectThreadPlanList //------------------------------------------------------------------------- static OptionDefinition g_thread_plan_list_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "verbose", 'v', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Display more information about the thread plans" }, { LLDB_OPT_SET_1, false, "internal", 'i', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Display internal as well as user thread plans" } // clang-format on }; class CommandObjectThreadPlanList : public CommandObjectIterateOverThreads { public: class CommandOptions : public Options { public: CommandOptions() : Options() { // Keep default values of all options in one place: OptionParsingStarting // () OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'i': m_internal = true; break; case 'v': m_verbose = true; break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_verbose = false; m_internal = false; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_thread_plan_list_options); } // Instance variables to hold the values for command options. bool m_verbose; bool m_internal; }; CommandObjectThreadPlanList(CommandInterpreter &interpreter) : CommandObjectIterateOverThreads( interpreter, "thread plan list", "Show thread plans for one or more threads. If no threads are " "specified, show the " "current thread. Use the thread-index \"all\" to see all threads.", nullptr, eCommandRequiresProcess | eCommandRequiresThread | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() {} ~CommandObjectThreadPlanList() override = default; Options *GetOptions() override { return &m_options; } protected: bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override { ThreadSP thread_sp = m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid); if (!thread_sp) { result.AppendErrorWithFormat("thread no longer exists: 0x%" PRIx64 "\n", tid); result.SetStatus(eReturnStatusFailed); return false; } Thread *thread = thread_sp.get(); Stream &strm = result.GetOutputStream(); DescriptionLevel desc_level = eDescriptionLevelFull; if (m_options.m_verbose) desc_level = eDescriptionLevelVerbose; thread->DumpThreadPlans(&strm, desc_level, m_options.m_internal, true); return true; } CommandOptions m_options; }; class CommandObjectThreadPlanDiscard : public CommandObjectParsed { public: CommandObjectThreadPlanDiscard(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "thread plan discard", "Discards thread plans up to and including the " "specified index (see 'thread plan list'.) " "Only user visible plans can be discarded.", nullptr, eCommandRequiresProcess | eCommandRequiresThread | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) { CommandArgumentEntry arg; CommandArgumentData plan_index_arg; // Define the first (and only) variant of this arg. plan_index_arg.arg_type = eArgTypeUnsignedInteger; plan_index_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(plan_index_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectThreadPlanDiscard() override = default; bool DoExecute(Args &args, CommandReturnObject &result) override { Thread *thread = m_exe_ctx.GetThreadPtr(); if (args.GetArgumentCount() != 1) { result.AppendErrorWithFormat("Too many arguments, expected one - the " "thread plan index - but got %zu.", args.GetArgumentCount()); result.SetStatus(eReturnStatusFailed); return false; } bool success; uint32_t thread_plan_idx = StringConvert::ToUInt32(args.GetArgumentAtIndex(0), 0, 0, &success); if (!success) { result.AppendErrorWithFormat( "Invalid thread index: \"%s\" - should be unsigned int.", args.GetArgumentAtIndex(0)); result.SetStatus(eReturnStatusFailed); return false; } if (thread_plan_idx == 0) { result.AppendErrorWithFormat( "You wouldn't really want me to discard the base thread plan."); result.SetStatus(eReturnStatusFailed); return false; } if (thread->DiscardUserThreadPlansUpToIndex(thread_plan_idx)) { result.SetStatus(eReturnStatusSuccessFinishNoResult); return true; } else { result.AppendErrorWithFormat( "Could not find User thread plan with index %s.", args.GetArgumentAtIndex(0)); result.SetStatus(eReturnStatusFailed); return false; } } }; //------------------------------------------------------------------------- // CommandObjectMultiwordThreadPlan //------------------------------------------------------------------------- class CommandObjectMultiwordThreadPlan : public CommandObjectMultiword { public: CommandObjectMultiwordThreadPlan(CommandInterpreter &interpreter) : CommandObjectMultiword( interpreter, "plan", "Commands for managing thread plans that control execution.", "thread plan [ []") { LoadSubCommand("backtrace", CommandObjectSP(new CommandObjectThreadBacktrace( interpreter))); LoadSubCommand("continue", CommandObjectSP(new CommandObjectThreadContinue(interpreter))); LoadSubCommand("list", CommandObjectSP(new CommandObjectThreadList(interpreter))); LoadSubCommand("return", CommandObjectSP(new CommandObjectThreadReturn(interpreter))); LoadSubCommand("jump", CommandObjectSP(new CommandObjectThreadJump(interpreter))); LoadSubCommand("select", CommandObjectSP(new CommandObjectThreadSelect(interpreter))); LoadSubCommand("until", CommandObjectSP(new CommandObjectThreadUntil(interpreter))); LoadSubCommand("info", CommandObjectSP(new CommandObjectThreadInfo(interpreter))); LoadSubCommand("step-in", CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope( interpreter, "thread step-in", "Source level single step, stepping into calls. Defaults " "to current thread unless specified.", nullptr, eStepTypeInto, eStepScopeSource))); LoadSubCommand("step-out", CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope( interpreter, "thread step-out", "Finish executing the current stack frame and stop after " "returning. Defaults to current thread unless specified.", nullptr, eStepTypeOut, eStepScopeSource))); LoadSubCommand("step-over", CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope( interpreter, "thread step-over", "Source level single step, stepping over calls. Defaults " "to current thread unless specified.", nullptr, eStepTypeOver, eStepScopeSource))); LoadSubCommand("step-inst", CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope( interpreter, "thread step-inst", "Instruction level single step, stepping into calls. " "Defaults to current thread unless specified.", nullptr, eStepTypeTrace, eStepScopeInstruction))); LoadSubCommand("step-inst-over", CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope( interpreter, "thread step-inst-over", "Instruction level single step, stepping over calls. " "Defaults to current thread unless specified.", nullptr, eStepTypeTraceOver, eStepScopeInstruction))); LoadSubCommand( "step-scripted", CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope( interpreter, "thread step-scripted", "Step as instructed by the script class passed in the -C option.", nullptr, eStepTypeScripted, eStepScopeSource))); LoadSubCommand("plan", CommandObjectSP(new CommandObjectMultiwordThreadPlan( interpreter))); } CommandObjectMultiwordThread::~CommandObjectMultiwordThread() = default; Index: vendor/lldb/dist/source/Core/FormatEntity.cpp =================================================================== --- vendor/lldb/dist/source/Core/FormatEntity.cpp (revision 319149) +++ vendor/lldb/dist/source/Core/FormatEntity.cpp (revision 319150) @@ -1,2418 +1,2418 @@ //===-- FormatEntity.cpp ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Core/FormatEntity.h" #include "lldb/Core/Address.h" #include "lldb/Core/AddressRange.h" // for AddressRange #include "lldb/Core/ArchSpec.h" // for ArchSpec #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/RegisterValue.h" // for RegisterValue #include "lldb/Core/StructuredData.h" // for StructuredData::O... #include "lldb/Core/ValueObject.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/DataFormatters/FormatClasses.h" // for TypeNameSpecifier... #include "lldb/DataFormatters/FormatManager.h" #include "lldb/DataFormatters/TypeSummary.h" // for TypeSummaryImpl::... #include "lldb/Expression/ExpressionVariable.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/CompilerType.h" // for CompilerType #include "lldb/Symbol/Function.h" #include "lldb/Symbol/LineEntry.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" // for SymbolContext #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/ExecutionContextScope.h" // for ExecutionContextS... #include "lldb/Target/Language.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/AnsiTerminal.h" #include "lldb/Utility/ConstString.h" // for ConstString, oper... #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Log.h" // for Log #include "lldb/Utility/Logging.h" // for GetLogIfAllCatego... #include "lldb/Utility/SharingPtr.h" // for SharingPtr #include "lldb/Utility/Stream.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/StringList.h" // for StringList #include "lldb/lldb-defines.h" // for LLDB_INVALID_ADDRESS #include "lldb/lldb-forward.h" // for ValueObjectSP #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" // for Triple, Triple::O... #include "llvm/Support/Compiler.h" // for LLVM_FALLTHROUGH #include // for isxdigit #include // for PRIu64, PRIx64 #include // for shared_ptr, opera... #include // for sprintf #include // for strtoul #include // for size_t, strchr #include // for move #include // for pair namespace lldb_private { class ScriptInterpreter; } namespace lldb_private { struct RegisterInfo; } using namespace lldb; using namespace lldb_private; enum FileKind { FileError = 0, Basename, Dirname, Fullpath }; #define ENTRY(n, t, f) \ { \ n, nullptr, FormatEntity::Entry::Type::t, \ FormatEntity::Entry::FormatType::f, 0, 0, nullptr, false \ } #define ENTRY_VALUE(n, t, f, v) \ { \ n, nullptr, FormatEntity::Entry::Type::t, \ FormatEntity::Entry::FormatType::f, v, 0, nullptr, false \ } #define ENTRY_CHILDREN(n, t, f, c) \ { \ n, nullptr, FormatEntity::Entry::Type::t, \ FormatEntity::Entry::FormatType::f, 0, \ static_cast(llvm::array_lengthof(c)), c, false \ } #define ENTRY_CHILDREN_KEEP_SEP(n, t, f, c) \ { \ n, nullptr, FormatEntity::Entry::Type::t, \ FormatEntity::Entry::FormatType::f, 0, \ static_cast(llvm::array_lengthof(c)), c, true \ } #define ENTRY_STRING(n, s) \ { \ n, s, FormatEntity::Entry::Type::InsertString, \ FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false \ } static FormatEntity::Entry::Definition g_string_entry[] = { ENTRY("*", ParentString, None)}; static FormatEntity::Entry::Definition g_addr_entries[] = { ENTRY("load", AddressLoad, UInt64), ENTRY("file", AddressFile, UInt64), ENTRY("load", AddressLoadOrFile, UInt64), }; static FormatEntity::Entry::Definition g_file_child_entries[] = { ENTRY_VALUE("basename", ParentNumber, CString, FileKind::Basename), ENTRY_VALUE("dirname", ParentNumber, CString, FileKind::Dirname), ENTRY_VALUE("fullpath", ParentNumber, CString, FileKind::Fullpath)}; static FormatEntity::Entry::Definition g_frame_child_entries[] = { ENTRY("index", FrameIndex, UInt32), ENTRY("pc", FrameRegisterPC, UInt64), ENTRY("fp", FrameRegisterFP, UInt64), ENTRY("sp", FrameRegisterSP, UInt64), ENTRY("flags", FrameRegisterFlags, UInt64), ENTRY("no-debug", FrameNoDebug, None), ENTRY_CHILDREN("reg", FrameRegisterByName, UInt64, g_string_entry), }; static FormatEntity::Entry::Definition g_function_child_entries[] = { ENTRY("id", FunctionID, UInt64), ENTRY("name", FunctionName, CString), ENTRY("name-without-args", FunctionNameNoArgs, CString), ENTRY("name-with-args", FunctionNameWithArgs, CString), ENTRY("addr-offset", FunctionAddrOffset, UInt64), ENTRY("concrete-only-addr-offset-no-padding", FunctionAddrOffsetConcrete, UInt64), ENTRY("line-offset", FunctionLineOffset, UInt64), ENTRY("pc-offset", FunctionPCOffset, UInt64), ENTRY("initial-function", FunctionInitial, None), ENTRY("changed", FunctionChanged, None), ENTRY("is-optimized", FunctionIsOptimized, None)}; static FormatEntity::Entry::Definition g_line_child_entries[] = { ENTRY_CHILDREN("file", LineEntryFile, None, g_file_child_entries), ENTRY("number", LineEntryLineNumber, UInt32), ENTRY("start-addr", LineEntryStartAddress, UInt64), ENTRY("end-addr", LineEntryEndAddress, UInt64), }; static FormatEntity::Entry::Definition g_module_child_entries[] = { ENTRY_CHILDREN("file", ModuleFile, None, g_file_child_entries), }; static FormatEntity::Entry::Definition g_process_child_entries[] = { ENTRY("id", ProcessID, UInt64), ENTRY_VALUE("name", ProcessFile, CString, FileKind::Basename), ENTRY_CHILDREN("file", ProcessFile, None, g_file_child_entries), }; static FormatEntity::Entry::Definition g_svar_child_entries[] = { ENTRY("*", ParentString, None)}; static FormatEntity::Entry::Definition g_var_child_entries[] = { ENTRY("*", ParentString, None)}; static FormatEntity::Entry::Definition g_thread_child_entries[] = { ENTRY("id", ThreadID, UInt64), ENTRY("protocol_id", ThreadProtocolID, UInt64), ENTRY("index", ThreadIndexID, UInt32), ENTRY_CHILDREN("info", ThreadInfo, None, g_string_entry), ENTRY("queue", ThreadQueue, CString), ENTRY("name", ThreadName, CString), ENTRY("stop-reason", ThreadStopReason, CString), ENTRY("return-value", ThreadReturnValue, CString), ENTRY("completed-expression", ThreadCompletedExpression, CString), }; static FormatEntity::Entry::Definition g_target_child_entries[] = { ENTRY("arch", TargetArch, CString), }; #define _TO_STR2(_val) #_val #define _TO_STR(_val) _TO_STR2(_val) static FormatEntity::Entry::Definition g_ansi_fg_entries[] = { ENTRY_STRING("black", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLACK) ANSI_ESC_END), ENTRY_STRING("red", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_RED) ANSI_ESC_END), ENTRY_STRING("green", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_GREEN) ANSI_ESC_END), ENTRY_STRING("yellow", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_YELLOW) ANSI_ESC_END), ENTRY_STRING("blue", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLUE) ANSI_ESC_END), ENTRY_STRING("purple", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_PURPLE) ANSI_ESC_END), ENTRY_STRING("cyan", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_CYAN) ANSI_ESC_END), ENTRY_STRING("white", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_WHITE) ANSI_ESC_END), }; static FormatEntity::Entry::Definition g_ansi_bg_entries[] = { ENTRY_STRING("black", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLACK) ANSI_ESC_END), ENTRY_STRING("red", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_RED) ANSI_ESC_END), ENTRY_STRING("green", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_GREEN) ANSI_ESC_END), ENTRY_STRING("yellow", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_YELLOW) ANSI_ESC_END), ENTRY_STRING("blue", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLUE) ANSI_ESC_END), ENTRY_STRING("purple", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_PURPLE) ANSI_ESC_END), ENTRY_STRING("cyan", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_CYAN) ANSI_ESC_END), ENTRY_STRING("white", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_WHITE) ANSI_ESC_END), }; static FormatEntity::Entry::Definition g_ansi_entries[] = { ENTRY_CHILDREN("fg", Invalid, None, g_ansi_fg_entries), ENTRY_CHILDREN("bg", Invalid, None, g_ansi_bg_entries), ENTRY_STRING("normal", ANSI_ESC_START _TO_STR(ANSI_CTRL_NORMAL) ANSI_ESC_END), ENTRY_STRING("bold", ANSI_ESC_START _TO_STR(ANSI_CTRL_BOLD) ANSI_ESC_END), ENTRY_STRING("faint", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAINT) ANSI_ESC_END), ENTRY_STRING("italic", ANSI_ESC_START _TO_STR(ANSI_CTRL_ITALIC) ANSI_ESC_END), ENTRY_STRING("underline", ANSI_ESC_START _TO_STR(ANSI_CTRL_UNDERLINE) ANSI_ESC_END), ENTRY_STRING("slow-blink", ANSI_ESC_START _TO_STR(ANSI_CTRL_SLOW_BLINK) ANSI_ESC_END), ENTRY_STRING("fast-blink", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAST_BLINK) ANSI_ESC_END), ENTRY_STRING("negative", ANSI_ESC_START _TO_STR(ANSI_CTRL_IMAGE_NEGATIVE) ANSI_ESC_END), ENTRY_STRING("conceal", ANSI_ESC_START _TO_STR(ANSI_CTRL_CONCEAL) ANSI_ESC_END), ENTRY_STRING("crossed-out", ANSI_ESC_START _TO_STR(ANSI_CTRL_CROSSED_OUT) ANSI_ESC_END), }; static FormatEntity::Entry::Definition g_script_child_entries[] = { ENTRY("frame", ScriptFrame, None), ENTRY("process", ScriptProcess, None), ENTRY("target", ScriptTarget, None), ENTRY("thread", ScriptThread, None), ENTRY("var", ScriptVariable, None), ENTRY("svar", ScriptVariableSynthetic, None), ENTRY("thread", ScriptThread, None), }; static FormatEntity::Entry::Definition g_top_level_entries[] = { ENTRY_CHILDREN("addr", AddressLoadOrFile, UInt64, g_addr_entries), ENTRY("addr-file-or-load", AddressLoadOrFile, UInt64), ENTRY_CHILDREN("ansi", Invalid, None, g_ansi_entries), ENTRY("current-pc-arrow", CurrentPCArrow, CString), ENTRY_CHILDREN("file", File, CString, g_file_child_entries), ENTRY("language", Lang, CString), ENTRY_CHILDREN("frame", Invalid, None, g_frame_child_entries), ENTRY_CHILDREN("function", Invalid, None, g_function_child_entries), ENTRY_CHILDREN("line", Invalid, None, g_line_child_entries), ENTRY_CHILDREN("module", Invalid, None, g_module_child_entries), ENTRY_CHILDREN("process", Invalid, None, g_process_child_entries), ENTRY_CHILDREN("script", Invalid, None, g_script_child_entries), ENTRY_CHILDREN_KEEP_SEP("svar", VariableSynthetic, None, g_svar_child_entries), ENTRY_CHILDREN("thread", Invalid, None, g_thread_child_entries), ENTRY_CHILDREN("target", Invalid, None, g_target_child_entries), ENTRY_CHILDREN_KEEP_SEP("var", Variable, None, g_var_child_entries), }; static FormatEntity::Entry::Definition g_root = ENTRY_CHILDREN("", Root, None, g_top_level_entries); FormatEntity::Entry::Entry(llvm::StringRef s) : string(s.data(), s.size()), printf_format(), children(), definition(nullptr), type(Type::String), fmt(lldb::eFormatDefault), number(0), deref(false) {} FormatEntity::Entry::Entry(char ch) : string(1, ch), printf_format(), children(), definition(nullptr), type(Type::String), fmt(lldb::eFormatDefault), number(0), deref(false) {} void FormatEntity::Entry::AppendChar(char ch) { if (children.empty() || children.back().type != Entry::Type::String) children.push_back(Entry(ch)); else children.back().string.append(1, ch); } void FormatEntity::Entry::AppendText(const llvm::StringRef &s) { if (children.empty() || children.back().type != Entry::Type::String) children.push_back(Entry(s)); else children.back().string.append(s.data(), s.size()); } void FormatEntity::Entry::AppendText(const char *cstr) { return AppendText(llvm::StringRef(cstr)); } Status FormatEntity::Parse(const llvm::StringRef &format_str, Entry &entry) { entry.Clear(); entry.type = Entry::Type::Root; llvm::StringRef modifiable_format(format_str); return ParseInternal(modifiable_format, entry, 0); } #define ENUM_TO_CSTR(eee) \ case FormatEntity::Entry::Type::eee: \ return #eee const char *FormatEntity::Entry::TypeToCString(Type t) { switch (t) { ENUM_TO_CSTR(Invalid); ENUM_TO_CSTR(ParentNumber); ENUM_TO_CSTR(ParentString); ENUM_TO_CSTR(InsertString); ENUM_TO_CSTR(Root); ENUM_TO_CSTR(String); ENUM_TO_CSTR(Scope); ENUM_TO_CSTR(Variable); ENUM_TO_CSTR(VariableSynthetic); ENUM_TO_CSTR(ScriptVariable); ENUM_TO_CSTR(ScriptVariableSynthetic); ENUM_TO_CSTR(AddressLoad); ENUM_TO_CSTR(AddressFile); ENUM_TO_CSTR(AddressLoadOrFile); ENUM_TO_CSTR(ProcessID); ENUM_TO_CSTR(ProcessFile); ENUM_TO_CSTR(ScriptProcess); ENUM_TO_CSTR(ThreadID); ENUM_TO_CSTR(ThreadProtocolID); ENUM_TO_CSTR(ThreadIndexID); ENUM_TO_CSTR(ThreadName); ENUM_TO_CSTR(ThreadQueue); ENUM_TO_CSTR(ThreadStopReason); ENUM_TO_CSTR(ThreadReturnValue); ENUM_TO_CSTR(ThreadCompletedExpression); ENUM_TO_CSTR(ScriptThread); ENUM_TO_CSTR(ThreadInfo); ENUM_TO_CSTR(TargetArch); ENUM_TO_CSTR(ScriptTarget); ENUM_TO_CSTR(ModuleFile); ENUM_TO_CSTR(File); ENUM_TO_CSTR(Lang); ENUM_TO_CSTR(FrameIndex); ENUM_TO_CSTR(FrameNoDebug); ENUM_TO_CSTR(FrameRegisterPC); ENUM_TO_CSTR(FrameRegisterSP); ENUM_TO_CSTR(FrameRegisterFP); ENUM_TO_CSTR(FrameRegisterFlags); ENUM_TO_CSTR(FrameRegisterByName); ENUM_TO_CSTR(ScriptFrame); ENUM_TO_CSTR(FunctionID); ENUM_TO_CSTR(FunctionDidChange); ENUM_TO_CSTR(FunctionInitialFunction); ENUM_TO_CSTR(FunctionName); ENUM_TO_CSTR(FunctionNameWithArgs); ENUM_TO_CSTR(FunctionNameNoArgs); ENUM_TO_CSTR(FunctionAddrOffset); ENUM_TO_CSTR(FunctionAddrOffsetConcrete); ENUM_TO_CSTR(FunctionLineOffset); ENUM_TO_CSTR(FunctionPCOffset); ENUM_TO_CSTR(FunctionInitial); ENUM_TO_CSTR(FunctionChanged); ENUM_TO_CSTR(FunctionIsOptimized); ENUM_TO_CSTR(LineEntryFile); ENUM_TO_CSTR(LineEntryLineNumber); ENUM_TO_CSTR(LineEntryStartAddress); ENUM_TO_CSTR(LineEntryEndAddress); ENUM_TO_CSTR(CurrentPCArrow); } return "???"; } #undef ENUM_TO_CSTR void FormatEntity::Entry::Dump(Stream &s, int depth) const { s.Printf("%*.*s%-20s: ", depth * 2, depth * 2, "", TypeToCString(type)); if (fmt != eFormatDefault) s.Printf("lldb-format = %s, ", FormatManager::GetFormatAsCString(fmt)); if (!string.empty()) s.Printf("string = \"%s\"", string.c_str()); if (!printf_format.empty()) s.Printf("printf_format = \"%s\"", printf_format.c_str()); if (number != 0) s.Printf("number = %" PRIu64 " (0x%" PRIx64 "), ", number, number); if (deref) s.Printf("deref = true, "); s.EOL(); for (const auto &child : children) { child.Dump(s, depth + 1); } } template static bool RunScriptFormatKeyword(Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, T t, const char *script_function_name) { Target *target = Target::GetTargetFromContexts(exe_ctx, sc); if (target) { ScriptInterpreter *script_interpreter = target->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); if (script_interpreter) { Status error; std::string script_output; if (script_interpreter->RunScriptFormatKeyword(script_function_name, t, script_output, error) && error.Success()) { s.Printf("%s", script_output.c_str()); return true; } else { s.Printf("", error.AsCString()); } } } return false; } static bool DumpAddress(Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, const Address &addr, bool print_file_addr_or_load_addr) { Target *target = Target::GetTargetFromContexts(exe_ctx, sc); addr_t vaddr = LLDB_INVALID_ADDRESS; if (exe_ctx && !target->GetSectionLoadList().IsEmpty()) vaddr = addr.GetLoadAddress(target); if (vaddr == LLDB_INVALID_ADDRESS) vaddr = addr.GetFileAddress(); if (vaddr != LLDB_INVALID_ADDRESS) { int addr_width = 0; if (exe_ctx && target) { addr_width = target->GetArchitecture().GetAddressByteSize() * 2; } if (addr_width == 0) addr_width = 16; if (print_file_addr_or_load_addr) { ExecutionContextScope *exe_scope = nullptr; if (exe_ctx) exe_scope = exe_ctx->GetBestExecutionContextScope(); addr.Dump(&s, exe_scope, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress, 0); } else { s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr); } return true; } return false; } static bool DumpAddressOffsetFromFunction(Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, const Address &format_addr, bool concrete_only, bool no_padding, bool print_zero_offsets) { if (format_addr.IsValid()) { Address func_addr; if (sc) { if (sc->function) { func_addr = sc->function->GetAddressRange().GetBaseAddress(); if (sc->block && !concrete_only) { // Check to make sure we aren't in an inline // function. If we are, use the inline block // range that contains "format_addr" since // blocks can be discontiguous. Block *inline_block = sc->block->GetContainingInlinedBlock(); AddressRange inline_range; if (inline_block && inline_block->GetRangeContainingAddress(format_addr, inline_range)) func_addr = inline_range.GetBaseAddress(); } } else if (sc->symbol && sc->symbol->ValueIsAddress()) func_addr = sc->symbol->GetAddressRef(); } if (func_addr.IsValid()) { const char *addr_offset_padding = no_padding ? "" : " "; if (func_addr.GetSection() == format_addr.GetSection()) { addr_t func_file_addr = func_addr.GetFileAddress(); addr_t addr_file_addr = format_addr.GetFileAddress(); if (addr_file_addr > func_file_addr || (addr_file_addr == func_file_addr && print_zero_offsets)) { s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding, addr_file_addr - func_file_addr); } else if (addr_file_addr < func_file_addr) { s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding, func_file_addr - addr_file_addr); } return true; } else { Target *target = Target::GetTargetFromContexts(exe_ctx, sc); if (target) { addr_t func_load_addr = func_addr.GetLoadAddress(target); addr_t addr_load_addr = format_addr.GetLoadAddress(target); if (addr_load_addr > func_load_addr || (addr_load_addr == func_load_addr && print_zero_offsets)) { s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding, addr_load_addr - func_load_addr); } else if (addr_load_addr < func_load_addr) { s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding, func_load_addr - addr_load_addr); } return true; } } } } return false; } static bool ScanBracketedRange(llvm::StringRef subpath, size_t &close_bracket_index, const char *&var_name_final_if_array_range, int64_t &index_lower, int64_t &index_higher) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS)); close_bracket_index = llvm::StringRef::npos; const size_t open_bracket_index = subpath.find('['); if (open_bracket_index == llvm::StringRef::npos) { if (log) log->Printf("[ScanBracketedRange] no bracketed range, skipping entirely"); return false; } close_bracket_index = subpath.find(']', open_bracket_index + 1); if (close_bracket_index == llvm::StringRef::npos) { if (log) log->Printf("[ScanBracketedRange] no bracketed range, skipping entirely"); return false; } else { var_name_final_if_array_range = subpath.data() + open_bracket_index; if (close_bracket_index - open_bracket_index == 1) { if (log) log->Printf( "[ScanBracketedRange] '[]' detected.. going from 0 to end of data"); index_lower = 0; } else { const size_t separator_index = subpath.find('-', open_bracket_index + 1); if (separator_index == llvm::StringRef::npos) { const char *index_lower_cstr = subpath.data() + open_bracket_index + 1; index_lower = ::strtoul(index_lower_cstr, nullptr, 0); index_higher = index_lower; if (log) log->Printf("[ScanBracketedRange] [%" PRId64 "] detected, high index is same", index_lower); } else { const char *index_lower_cstr = subpath.data() + open_bracket_index + 1; const char *index_higher_cstr = subpath.data() + separator_index + 1; index_lower = ::strtoul(index_lower_cstr, nullptr, 0); index_higher = ::strtoul(index_higher_cstr, nullptr, 0); if (log) log->Printf("[ScanBracketedRange] [%" PRId64 "-%" PRId64 "] detected", index_lower, index_higher); } if (index_lower > index_higher && index_higher > 0) { if (log) log->Printf("[ScanBracketedRange] swapping indices"); const int64_t temp = index_lower; index_lower = index_higher; index_higher = temp; } } } return true; } static bool DumpFile(Stream &s, const FileSpec &file, FileKind file_kind) { switch (file_kind) { case FileKind::FileError: break; case FileKind::Basename: if (file.GetFilename()) { s << file.GetFilename(); return true; } break; case FileKind::Dirname: if (file.GetDirectory()) { s << file.GetDirectory(); return true; } break; case FileKind::Fullpath: if (file) { s << file; return true; } break; } return false; } static bool DumpRegister(Stream &s, StackFrame *frame, RegisterKind reg_kind, uint32_t reg_num, Format format) { if (frame) { RegisterContext *reg_ctx = frame->GetRegisterContext().get(); if (reg_ctx) { const uint32_t lldb_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); if (lldb_reg_num != LLDB_INVALID_REGNUM) { const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(lldb_reg_num); if (reg_info) { RegisterValue reg_value; if (reg_ctx->ReadRegister(reg_info, reg_value)) { reg_value.Dump(&s, reg_info, false, false, format); return true; } } } } } return false; } static ValueObjectSP ExpandIndexedExpression(ValueObject *valobj, size_t index, StackFrame *frame, bool deref_pointer) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS)); const char *ptr_deref_format = "[%d]"; std::string ptr_deref_buffer(10, 0); ::sprintf(&ptr_deref_buffer[0], ptr_deref_format, index); if (log) log->Printf("[ExpandIndexedExpression] name to deref: %s", ptr_deref_buffer.c_str()); ValueObject::GetValueForExpressionPathOptions options; ValueObject::ExpressionPathEndResultType final_value_type; ValueObject::ExpressionPathScanEndReason reason_to_stop; ValueObject::ExpressionPathAftermath what_next = (deref_pointer ? ValueObject::eExpressionPathAftermathDereference : ValueObject::eExpressionPathAftermathNothing); ValueObjectSP item = valobj->GetValueForExpressionPath( ptr_deref_buffer.c_str(), &reason_to_stop, &final_value_type, options, &what_next); if (!item) { if (log) log->Printf("[ExpandIndexedExpression] ERROR: why stopping = %d," " final_value_type %d", reason_to_stop, final_value_type); } else { if (log) log->Printf("[ExpandIndexedExpression] ALL RIGHT: why stopping = %d," " final_value_type %d", reason_to_stop, final_value_type); } return item; } static char ConvertValueObjectStyleToChar( ValueObject::ValueObjectRepresentationStyle style) { switch (style) { case ValueObject::eValueObjectRepresentationStyleLanguageSpecific: return '@'; case ValueObject::eValueObjectRepresentationStyleValue: return 'V'; case ValueObject::eValueObjectRepresentationStyleLocation: return 'L'; case ValueObject::eValueObjectRepresentationStyleSummary: return 'S'; case ValueObject::eValueObjectRepresentationStyleChildrenCount: return '#'; case ValueObject::eValueObjectRepresentationStyleType: return 'T'; case ValueObject::eValueObjectRepresentationStyleName: return 'N'; case ValueObject::eValueObjectRepresentationStyleExpressionPath: return '>'; } return '\0'; } static bool DumpValue(Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, const FormatEntity::Entry &entry, ValueObject *valobj) { if (valobj == nullptr) return false; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS)); Format custom_format = eFormatInvalid; ValueObject::ValueObjectRepresentationStyle val_obj_display = entry.string.empty() ? ValueObject::eValueObjectRepresentationStyleValue : ValueObject::eValueObjectRepresentationStyleSummary; bool do_deref_pointer = entry.deref; bool is_script = false; switch (entry.type) { case FormatEntity::Entry::Type::ScriptVariable: is_script = true; break; case FormatEntity::Entry::Type::Variable: custom_format = entry.fmt; val_obj_display = (ValueObject::ValueObjectRepresentationStyle)entry.number; break; case FormatEntity::Entry::Type::ScriptVariableSynthetic: is_script = true; LLVM_FALLTHROUGH; case FormatEntity::Entry::Type::VariableSynthetic: custom_format = entry.fmt; val_obj_display = (ValueObject::ValueObjectRepresentationStyle)entry.number; if (!valobj->IsSynthetic()) { valobj = valobj->GetSyntheticValue().get(); if (valobj == nullptr) return false; } break; default: return false; } if (valobj == nullptr) return false; ValueObject::ExpressionPathAftermath what_next = (do_deref_pointer ? ValueObject::eExpressionPathAftermathDereference : ValueObject::eExpressionPathAftermathNothing); ValueObject::GetValueForExpressionPathOptions options; options.DontCheckDotVsArrowSyntax() .DoAllowBitfieldSyntax() .DoAllowFragileIVar() .SetSyntheticChildrenTraversal( ValueObject::GetValueForExpressionPathOptions:: SyntheticChildrenTraversal::Both); ValueObject *target = nullptr; const char *var_name_final_if_array_range = nullptr; size_t close_bracket_index = llvm::StringRef::npos; int64_t index_lower = -1; int64_t index_higher = -1; bool is_array_range = false; bool was_plain_var = false; bool was_var_format = false; bool was_var_indexed = false; ValueObject::ExpressionPathScanEndReason reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; ValueObject::ExpressionPathEndResultType final_value_type = ValueObject::eExpressionPathEndResultTypePlain; if (is_script) { return RunScriptFormatKeyword(s, sc, exe_ctx, valobj, entry.string.c_str()); } llvm::StringRef subpath(entry.string); // simplest case ${var}, just print valobj's value if (entry.string.empty()) { if (entry.printf_format.empty() && entry.fmt == eFormatDefault && entry.number == ValueObject::eValueObjectRepresentationStyleValue) was_plain_var = true; else was_var_format = true; target = valobj; } else // this is ${var.something} or multiple .something nested { if (entry.string[0] == '[') was_var_indexed = true; ScanBracketedRange(subpath, close_bracket_index, var_name_final_if_array_range, index_lower, index_higher); Status error; const std::string &expr_path = entry.string; if (log) log->Printf("[Debugger::FormatPrompt] symbol to expand: %s", expr_path.c_str()); target = valobj ->GetValueForExpressionPath(expr_path.c_str(), &reason_to_stop, &final_value_type, options, &what_next) .get(); if (!target) { if (log) log->Printf("[Debugger::FormatPrompt] ERROR: why stopping = %d," " final_value_type %d", reason_to_stop, final_value_type); return false; } else { if (log) log->Printf("[Debugger::FormatPrompt] ALL RIGHT: why stopping = %d," " final_value_type %d", reason_to_stop, final_value_type); target = target ->GetQualifiedRepresentationIfAvailable( target->GetDynamicValueType(), true) .get(); } } is_array_range = (final_value_type == ValueObject::eExpressionPathEndResultTypeBoundedRange || final_value_type == ValueObject::eExpressionPathEndResultTypeUnboundedRange); do_deref_pointer = (what_next == ValueObject::eExpressionPathAftermathDereference); if (do_deref_pointer && !is_array_range) { // I have not deref-ed yet, let's do it // this happens when we are not going through // GetValueForVariableExpressionPath // to get to the target ValueObject Status error; target = target->Dereference(error).get(); if (error.Fail()) { if (log) log->Printf("[Debugger::FormatPrompt] ERROR: %s\n", error.AsCString("unknown")); return false; } do_deref_pointer = false; } if (!target) { if (log) log->Printf("[Debugger::FormatPrompt] could not calculate target for " "prompt expression"); return false; } // we do not want to use the summary for a bitfield of type T:n // if we were originally dealing with just a T - that would get // us into an endless recursion if (target->IsBitfield() && was_var_indexed) { // TODO: check for a (T:n)-specific summary - we should still obey that StreamString bitfield_name; bitfield_name.Printf("%s:%d", target->GetTypeName().AsCString(), target->GetBitfieldBitSize()); auto type_sp = std::make_shared( bitfield_name.GetString(), false); if (val_obj_display == ValueObject::eValueObjectRepresentationStyleSummary && !DataVisualization::GetSummaryForType(type_sp)) val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; } // TODO use flags for these const uint32_t type_info_flags = target->GetCompilerType().GetTypeInfo(nullptr); bool is_array = (type_info_flags & eTypeIsArray) != 0; bool is_pointer = (type_info_flags & eTypeIsPointer) != 0; bool is_aggregate = target->GetCompilerType().IsAggregateType(); if ((is_array || is_pointer) && (!is_array_range) && val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) // this should be // wrong, but there // are some // exceptions { StreamString str_temp; if (log) log->Printf( "[Debugger::FormatPrompt] I am into array || pointer && !range"); if (target->HasSpecialPrintableRepresentation(val_obj_display, custom_format)) { // try to use the special cases bool success = target->DumpPrintableRepresentation( str_temp, val_obj_display, custom_format); if (log) log->Printf("[Debugger::FormatPrompt] special cases did%s match", success ? "" : "n't"); // should not happen if (success) s << str_temp.GetString(); return true; } else { if (was_plain_var) // if ${var} { s << target->GetTypeName() << " @ " << target->GetLocationAsCString(); } else if (is_pointer) // if pointer, value is the address stored { target->DumpPrintableRepresentation( s, val_obj_display, custom_format, ValueObject::PrintableRepresentationSpecialCases::eDisable); } return true; } } // if directly trying to print ${var}, and this is an aggregate, display a // nice // type @ location message if (is_aggregate && was_plain_var) { s << target->GetTypeName() << " @ " << target->GetLocationAsCString(); return true; } // if directly trying to print ${var%V}, and this is an aggregate, do not let // the user do it if (is_aggregate && ((was_var_format && val_obj_display == ValueObject::eValueObjectRepresentationStyleValue))) { s << ""; return true; } if (!is_array_range) { if (log) log->Printf("[Debugger::FormatPrompt] dumping ordinary printable output"); return target->DumpPrintableRepresentation(s, val_obj_display, custom_format); } else { if (log) log->Printf("[Debugger::FormatPrompt] checking if I can handle as array"); if (!is_array && !is_pointer) return false; if (log) log->Printf("[Debugger::FormatPrompt] handle as array"); StreamString special_directions_stream; llvm::StringRef special_directions; if (close_bracket_index != llvm::StringRef::npos && subpath.size() > close_bracket_index) { ConstString additional_data(subpath.drop_front(close_bracket_index + 1)); special_directions_stream.Printf("${%svar%s", do_deref_pointer ? "*" : "", additional_data.GetCString()); if (entry.fmt != eFormatDefault) { const char format_char = FormatManager::GetFormatAsFormatChar(entry.fmt); if (format_char != '\0') special_directions_stream.Printf("%%%c", format_char); else { const char *format_cstr = FormatManager::GetFormatAsCString(entry.fmt); special_directions_stream.Printf("%%%s", format_cstr); } } else if (entry.number != 0) { const char style_char = ConvertValueObjectStyleToChar( (ValueObject::ValueObjectRepresentationStyle)entry.number); if (style_char) special_directions_stream.Printf("%%%c", style_char); } special_directions_stream.PutChar('}'); special_directions = llvm::StringRef(special_directions_stream.GetString()); } // let us display items index_lower thru index_higher of this array s.PutChar('['); if (index_higher < 0) index_higher = valobj->GetNumChildren() - 1; uint32_t max_num_children = target->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); bool success = true; for (int64_t index = index_lower; index <= index_higher; ++index) { ValueObject *item = ExpandIndexedExpression(target, index, exe_ctx->GetFramePtr(), false) .get(); if (!item) { if (log) log->Printf("[Debugger::FormatPrompt] ERROR in getting child item at " "index %" PRId64, index); } else { if (log) log->Printf( "[Debugger::FormatPrompt] special_directions for child item: %s", special_directions.data() ? special_directions.data() : ""); } if (special_directions.empty()) { success &= item->DumpPrintableRepresentation(s, val_obj_display, custom_format); } else { success &= FormatEntity::FormatStringRef( special_directions, s, sc, exe_ctx, nullptr, item, false, false); } if (--max_num_children == 0) { s.PutCString(", ..."); break; } if (index < index_higher) s.PutChar(','); } s.PutChar(']'); return success; } } static bool DumpRegister(Stream &s, StackFrame *frame, const char *reg_name, Format format) { if (frame) { RegisterContext *reg_ctx = frame->GetRegisterContext().get(); if (reg_ctx) { const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name); if (reg_info) { RegisterValue reg_value; if (reg_ctx->ReadRegister(reg_info, reg_value)) { reg_value.Dump(&s, reg_info, false, false, format); return true; } } } } return false; } static bool FormatThreadExtendedInfoRecurse( const FormatEntity::Entry &entry, const StructuredData::ObjectSP &thread_info_dictionary, const SymbolContext *sc, const ExecutionContext *exe_ctx, Stream &s) { llvm::StringRef path(entry.string); StructuredData::ObjectSP value = thread_info_dictionary->GetObjectForDotSeparatedPath(path); if (value) { - if (value->GetType() == StructuredData::Type::eTypeInteger) { + if (value->GetType() == eStructuredDataTypeInteger) { const char *token_format = "0x%4.4" PRIx64; if (!entry.printf_format.empty()) token_format = entry.printf_format.c_str(); s.Printf(token_format, value->GetAsInteger()->GetValue()); return true; - } else if (value->GetType() == StructuredData::Type::eTypeFloat) { + } else if (value->GetType() == eStructuredDataTypeFloat) { s.Printf("%f", value->GetAsFloat()->GetValue()); return true; - } else if (value->GetType() == StructuredData::Type::eTypeString) { + } else if (value->GetType() == eStructuredDataTypeString) { s.Format("{0}", value->GetAsString()->GetValue()); return true; - } else if (value->GetType() == StructuredData::Type::eTypeArray) { + } else if (value->GetType() == eStructuredDataTypeArray) { if (value->GetAsArray()->GetSize() > 0) { s.Printf("%zu", value->GetAsArray()->GetSize()); return true; } - } else if (value->GetType() == StructuredData::Type::eTypeDictionary) { + } else if (value->GetType() == eStructuredDataTypeDictionary) { s.Printf("%zu", value->GetAsDictionary()->GetKeys()->GetAsArray()->GetSize()); return true; } } return false; } static inline bool IsToken(const char *var_name_begin, const char *var) { return (::strncmp(var_name_begin, var, strlen(var)) == 0); } bool FormatEntity::FormatStringRef(const llvm::StringRef &format_str, Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, const Address *addr, ValueObject *valobj, bool function_changed, bool initial_function) { if (!format_str.empty()) { FormatEntity::Entry root; Status error = FormatEntity::Parse(format_str, root); if (error.Success()) { return FormatEntity::Format(root, s, sc, exe_ctx, addr, valobj, function_changed, initial_function); } } return false; } bool FormatEntity::FormatCString(const char *format, Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, const Address *addr, ValueObject *valobj, bool function_changed, bool initial_function) { if (format && format[0]) { FormatEntity::Entry root; llvm::StringRef format_str(format); Status error = FormatEntity::Parse(format_str, root); if (error.Success()) { return FormatEntity::Format(root, s, sc, exe_ctx, addr, valobj, function_changed, initial_function); } } return false; } bool FormatEntity::Format(const Entry &entry, Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, const Address *addr, ValueObject *valobj, bool function_changed, bool initial_function) { switch (entry.type) { case Entry::Type::Invalid: case Entry::Type::ParentNumber: // Only used for // FormatEntity::Entry::Definition encoding case Entry::Type::ParentString: // Only used for // FormatEntity::Entry::Definition encoding case Entry::Type::InsertString: // Only used for // FormatEntity::Entry::Definition encoding return false; case Entry::Type::Root: for (const auto &child : entry.children) { if (!Format(child, s, sc, exe_ctx, addr, valobj, function_changed, initial_function)) { return false; // If any item of root fails, then the formatting fails } } return true; // Only return true if all items succeeded case Entry::Type::String: s.PutCString(entry.string); return true; case Entry::Type::Scope: { StreamString scope_stream; bool success = false; for (const auto &child : entry.children) { success = Format(child, scope_stream, sc, exe_ctx, addr, valobj, function_changed, initial_function); if (!success) break; } // Only if all items in a scope succeed, then do we // print the output into the main stream if (success) s.Write(scope_stream.GetString().data(), scope_stream.GetString().size()); } return true; // Scopes always successfully print themselves case Entry::Type::Variable: case Entry::Type::VariableSynthetic: case Entry::Type::ScriptVariable: case Entry::Type::ScriptVariableSynthetic: return DumpValue(s, sc, exe_ctx, entry, valobj); case Entry::Type::AddressFile: case Entry::Type::AddressLoad: case Entry::Type::AddressLoadOrFile: return (addr != nullptr && addr->IsValid() && DumpAddress(s, sc, exe_ctx, *addr, entry.type == Entry::Type::AddressLoadOrFile)); case Entry::Type::ProcessID: if (exe_ctx) { Process *process = exe_ctx->GetProcessPtr(); if (process) { const char *format = "%" PRIu64; if (!entry.printf_format.empty()) format = entry.printf_format.c_str(); s.Printf(format, process->GetID()); return true; } } return false; case Entry::Type::ProcessFile: if (exe_ctx) { Process *process = exe_ctx->GetProcessPtr(); if (process) { Module *exe_module = process->GetTarget().GetExecutableModulePointer(); if (exe_module) { if (DumpFile(s, exe_module->GetFileSpec(), (FileKind)entry.number)) return true; } } } return false; case Entry::Type::ScriptProcess: if (exe_ctx) { Process *process = exe_ctx->GetProcessPtr(); if (process) return RunScriptFormatKeyword(s, sc, exe_ctx, process, entry.string.c_str()); } return false; case Entry::Type::ThreadID: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { const char *format = "0x%4.4" PRIx64; if (!entry.printf_format.empty()) { // Watch for the special "tid" format... if (entry.printf_format == "tid") { // TODO(zturner): Rather than hardcoding this to be platform // specific, it should be controlled by a // setting and the default value of the setting can be different // depending on the platform. Target &target = thread->GetProcess()->GetTarget(); ArchSpec arch(target.GetArchitecture()); llvm::Triple::OSType ostype = arch.IsValid() ? arch.GetTriple().getOS() : llvm::Triple::UnknownOS; if ((ostype == llvm::Triple::FreeBSD) || (ostype == llvm::Triple::Linux) || (ostype == llvm::Triple::NetBSD)) { format = "%" PRIu64; } } else { format = entry.printf_format.c_str(); } } s.Printf(format, thread->GetID()); return true; } } return false; case Entry::Type::ThreadProtocolID: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { const char *format = "0x%4.4" PRIx64; if (!entry.printf_format.empty()) format = entry.printf_format.c_str(); s.Printf(format, thread->GetProtocolID()); return true; } } return false; case Entry::Type::ThreadIndexID: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { const char *format = "%" PRIu32; if (!entry.printf_format.empty()) format = entry.printf_format.c_str(); s.Printf(format, thread->GetIndexID()); return true; } } return false; case Entry::Type::ThreadName: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { const char *cstr = thread->GetName(); if (cstr && cstr[0]) { s.PutCString(cstr); return true; } } } return false; case Entry::Type::ThreadQueue: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { const char *cstr = thread->GetQueueName(); if (cstr && cstr[0]) { s.PutCString(cstr); return true; } } } return false; case Entry::Type::ThreadStopReason: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { StopInfoSP stop_info_sp = thread->GetStopInfo(); if (stop_info_sp && stop_info_sp->IsValid()) { const char *cstr = stop_info_sp->GetDescription(); if (cstr && cstr[0]) { s.PutCString(cstr); return true; } } } } return false; case Entry::Type::ThreadReturnValue: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { StopInfoSP stop_info_sp = thread->GetStopInfo(); if (stop_info_sp && stop_info_sp->IsValid()) { ValueObjectSP return_valobj_sp = StopInfo::GetReturnValueObject(stop_info_sp); if (return_valobj_sp) { return_valobj_sp->Dump(s); return true; } } } } return false; case Entry::Type::ThreadCompletedExpression: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { StopInfoSP stop_info_sp = thread->GetStopInfo(); if (stop_info_sp && stop_info_sp->IsValid()) { ExpressionVariableSP expression_var_sp = StopInfo::GetExpressionVariable(stop_info_sp); if (expression_var_sp && expression_var_sp->GetValueObject()) { expression_var_sp->GetValueObject()->Dump(s); return true; } } } } return false; case Entry::Type::ScriptThread: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) return RunScriptFormatKeyword(s, sc, exe_ctx, thread, entry.string.c_str()); } return false; case Entry::Type::ThreadInfo: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { StructuredData::ObjectSP object_sp = thread->GetExtendedInfo(); if (object_sp && - object_sp->GetType() == StructuredData::Type::eTypeDictionary) { + object_sp->GetType() == eStructuredDataTypeDictionary) { if (FormatThreadExtendedInfoRecurse(entry, object_sp, sc, exe_ctx, s)) return true; } } } return false; case Entry::Type::TargetArch: if (exe_ctx) { Target *target = exe_ctx->GetTargetPtr(); if (target) { const ArchSpec &arch = target->GetArchitecture(); if (arch.IsValid()) { s.PutCString(arch.GetArchitectureName()); return true; } } } return false; case Entry::Type::ScriptTarget: if (exe_ctx) { Target *target = exe_ctx->GetTargetPtr(); if (target) return RunScriptFormatKeyword(s, sc, exe_ctx, target, entry.string.c_str()); } return false; case Entry::Type::ModuleFile: if (sc) { Module *module = sc->module_sp.get(); if (module) { if (DumpFile(s, module->GetFileSpec(), (FileKind)entry.number)) return true; } } return false; case Entry::Type::File: if (sc) { CompileUnit *cu = sc->comp_unit; if (cu) { // CompileUnit is a FileSpec if (DumpFile(s, *cu, (FileKind)entry.number)) return true; } } return false; case Entry::Type::Lang: if (sc) { CompileUnit *cu = sc->comp_unit; if (cu) { const char *lang_name = Language::GetNameForLanguageType(cu->GetLanguage()); if (lang_name) { s.PutCString(lang_name); return true; } } } return false; case Entry::Type::FrameIndex: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { const char *format = "%" PRIu32; if (!entry.printf_format.empty()) format = entry.printf_format.c_str(); s.Printf(format, frame->GetFrameIndex()); return true; } } return false; case Entry::Type::FrameRegisterPC: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { const Address &pc_addr = frame->GetFrameCodeAddress(); if (pc_addr.IsValid()) { if (DumpAddress(s, sc, exe_ctx, pc_addr, false)) return true; } } } return false; case Entry::Type::FrameRegisterSP: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, (lldb::Format)entry.number)) return true; } } return false; case Entry::Type::FrameRegisterFP: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP, (lldb::Format)entry.number)) return true; } } return false; case Entry::Type::FrameRegisterFlags: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, (lldb::Format)entry.number)) return true; } } return false; case Entry::Type::FrameNoDebug: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { return !frame->HasDebugInformation(); } } return true; case Entry::Type::FrameRegisterByName: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { if (DumpRegister(s, frame, entry.string.c_str(), (lldb::Format)entry.number)) return true; } } return false; case Entry::Type::ScriptFrame: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) return RunScriptFormatKeyword(s, sc, exe_ctx, frame, entry.string.c_str()); } return false; case Entry::Type::FunctionID: if (sc) { if (sc->function) { s.Printf("function{0x%8.8" PRIx64 "}", sc->function->GetID()); return true; } else if (sc->symbol) { s.Printf("symbol[%u]", sc->symbol->GetID()); return true; } } return false; case Entry::Type::FunctionDidChange: return function_changed; case Entry::Type::FunctionInitialFunction: return initial_function; case Entry::Type::FunctionName: { Language *language_plugin = nullptr; bool language_plugin_handled = false; StreamString ss; if (sc->function) language_plugin = Language::FindPlugin(sc->function->GetLanguage()); else if (sc->symbol) language_plugin = Language::FindPlugin(sc->symbol->GetLanguage()); if (language_plugin) { language_plugin_handled = language_plugin->GetFunctionDisplayName( sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss); } if (language_plugin_handled) { s << ss.GetString(); return true; } else { const char *name = nullptr; if (sc->function) name = sc->function->GetName().AsCString(nullptr); else if (sc->symbol) name = sc->symbol->GetName().AsCString(nullptr); if (name) { s.PutCString(name); if (sc->block) { Block *inline_block = sc->block->GetContainingInlinedBlock(); if (inline_block) { const InlineFunctionInfo *inline_info = sc->block->GetInlinedFunctionInfo(); if (inline_info) { s.PutCString(" [inlined] "); inline_info->GetName(sc->function->GetLanguage()).Dump(&s); } } } return true; } } } return false; case Entry::Type::FunctionNameNoArgs: { Language *language_plugin = nullptr; bool language_plugin_handled = false; StreamString ss; if (sc->function) language_plugin = Language::FindPlugin(sc->function->GetLanguage()); else if (sc->symbol) language_plugin = Language::FindPlugin(sc->symbol->GetLanguage()); if (language_plugin) { language_plugin_handled = language_plugin->GetFunctionDisplayName( sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs, ss); } if (language_plugin_handled) { s << ss.GetString(); return true; } else { ConstString name; if (sc->function) name = sc->function->GetNameNoArguments(); else if (sc->symbol) name = sc->symbol->GetNameNoArguments(); if (name) { s.PutCString(name.GetCString()); return true; } } } return false; case Entry::Type::FunctionNameWithArgs: { Language *language_plugin = nullptr; bool language_plugin_handled = false; StreamString ss; if (sc->function) language_plugin = Language::FindPlugin(sc->function->GetLanguage()); else if (sc->symbol) language_plugin = Language::FindPlugin(sc->symbol->GetLanguage()); if (language_plugin) { language_plugin_handled = language_plugin->GetFunctionDisplayName( sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss); } if (language_plugin_handled) { s << ss.GetString(); return true; } else { // Print the function name with arguments in it if (sc->function) { ExecutionContextScope *exe_scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; const char *cstr = sc->function->GetName().AsCString(nullptr); if (cstr) { const InlineFunctionInfo *inline_info = nullptr; VariableListSP variable_list_sp; bool get_function_vars = true; if (sc->block) { Block *inline_block = sc->block->GetContainingInlinedBlock(); if (inline_block) { get_function_vars = false; inline_info = sc->block->GetInlinedFunctionInfo(); if (inline_info) variable_list_sp = inline_block->GetBlockVariableList(true); } } if (get_function_vars) { variable_list_sp = sc->function->GetBlock(true).GetBlockVariableList(true); } if (inline_info) { s.PutCString(cstr); s.PutCString(" [inlined] "); cstr = inline_info->GetName(sc->function->GetLanguage()).GetCString(); } VariableList args; if (variable_list_sp) variable_list_sp->AppendVariablesWithScope( eValueTypeVariableArgument, args); if (args.GetSize() > 0) { const char *open_paren = strchr(cstr, '('); const char *close_paren = nullptr; const char *generic = strchr(cstr, '<'); // if before the arguments list begins there is a template sign // then scan to the end of the generic args before you try to find // the arguments list if (generic && open_paren && generic < open_paren) { int generic_depth = 1; ++generic; for (; *generic && generic_depth > 0; generic++) { if (*generic == '<') generic_depth++; if (*generic == '>') generic_depth--; } if (*generic) open_paren = strchr(generic, '('); else open_paren = nullptr; } if (open_paren) { if (IsToken(open_paren, "(anonymous namespace)")) { open_paren = strchr(open_paren + strlen("(anonymous namespace)"), '('); if (open_paren) close_paren = strchr(open_paren, ')'); } else close_paren = strchr(open_paren, ')'); } if (open_paren) s.Write(cstr, open_paren - cstr + 1); else { s.PutCString(cstr); s.PutChar('('); } const size_t num_args = args.GetSize(); for (size_t arg_idx = 0; arg_idx < num_args; ++arg_idx) { std::string buffer; VariableSP var_sp(args.GetVariableAtIndex(arg_idx)); ValueObjectSP var_value_sp( ValueObjectVariable::Create(exe_scope, var_sp)); StreamString ss; llvm::StringRef var_representation; const char *var_name = var_value_sp->GetName().GetCString(); if (var_value_sp->GetCompilerType().IsValid()) { if (var_value_sp && exe_scope->CalculateTarget()) var_value_sp = var_value_sp->GetQualifiedRepresentationIfAvailable( exe_scope->CalculateTarget() ->TargetProperties::GetPreferDynamicValue(), exe_scope->CalculateTarget() ->TargetProperties::GetEnableSyntheticValue()); if (var_value_sp->GetCompilerType().IsAggregateType() && DataVisualization::ShouldPrintAsOneLiner(*var_value_sp)) { static StringSummaryFormat format( TypeSummaryImpl::Flags() .SetHideItemNames(false) .SetShowMembersOneLiner(true), ""); format.FormatObject(var_value_sp.get(), buffer, TypeSummaryOptions()); var_representation = buffer; } else var_value_sp->DumpPrintableRepresentation( ss, ValueObject::ValueObjectRepresentationStyle:: eValueObjectRepresentationStyleSummary, eFormatDefault, ValueObject::PrintableRepresentationSpecialCases::eAllow, false); } if (!ss.GetString().empty()) var_representation = ss.GetString(); if (arg_idx > 0) s.PutCString(", "); if (var_value_sp->GetError().Success()) { if (!var_representation.empty()) s.Printf("%s=%s", var_name, var_representation.str().c_str()); else s.Printf("%s=%s at %s", var_name, var_value_sp->GetTypeName().GetCString(), var_value_sp->GetLocationAsCString()); } else s.Printf("%s=", var_name); } if (close_paren) s.PutCString(close_paren); else s.PutChar(')'); } else { s.PutCString(cstr); } return true; } } else if (sc->symbol) { const char *cstr = sc->symbol->GetName().AsCString(nullptr); if (cstr) { s.PutCString(cstr); return true; } } } } return false; case Entry::Type::FunctionAddrOffset: if (addr) { if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, *addr, false, false, false)) return true; } return false; case Entry::Type::FunctionAddrOffsetConcrete: if (addr) { if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, *addr, true, true, true)) return true; } return false; case Entry::Type::FunctionLineOffset: return (DumpAddressOffsetFromFunction(s, sc, exe_ctx, sc->line_entry.range.GetBaseAddress(), false, false, false)); case Entry::Type::FunctionPCOffset: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, frame->GetFrameCodeAddress(), false, false, false)) return true; } } return false; case Entry::Type::FunctionChanged: return function_changed; case Entry::Type::FunctionIsOptimized: { bool is_optimized = false; if (sc->function && sc->function->GetIsOptimized()) { is_optimized = true; } return is_optimized; } case Entry::Type::FunctionInitial: return initial_function; case Entry::Type::LineEntryFile: if (sc && sc->line_entry.IsValid()) { Module *module = sc->module_sp.get(); if (module) { if (DumpFile(s, sc->line_entry.file, (FileKind)entry.number)) return true; } } return false; case Entry::Type::LineEntryLineNumber: if (sc && sc->line_entry.IsValid()) { const char *format = "%" PRIu32; if (!entry.printf_format.empty()) format = entry.printf_format.c_str(); s.Printf(format, sc->line_entry.line); return true; } return false; case Entry::Type::LineEntryStartAddress: case Entry::Type::LineEntryEndAddress: if (sc && sc->line_entry.range.GetBaseAddress().IsValid()) { Address addr = sc->line_entry.range.GetBaseAddress(); if (entry.type == Entry::Type::LineEntryEndAddress) addr.Slide(sc->line_entry.range.GetByteSize()); if (DumpAddress(s, sc, exe_ctx, addr, false)) return true; } return false; case Entry::Type::CurrentPCArrow: if (addr && exe_ctx && exe_ctx->GetFramePtr()) { RegisterContextSP reg_ctx = exe_ctx->GetFramePtr()->GetRegisterContextSP(); if (reg_ctx) { addr_t pc_loadaddr = reg_ctx->GetPC(); if (pc_loadaddr != LLDB_INVALID_ADDRESS) { Address pc; pc.SetLoadAddress(pc_loadaddr, exe_ctx->GetTargetPtr()); if (pc == *addr) { s.Printf("-> "); return true; } } } s.Printf(" "); return true; } return false; } return false; } static bool DumpCommaSeparatedChildEntryNames( Stream &s, const FormatEntity::Entry::Definition *parent) { if (parent->children) { const size_t n = parent->num_children; for (size_t i = 0; i < n; ++i) { if (i > 0) s.PutCString(", "); s.Printf("\"%s\"", parent->children[i].name); } return true; } return false; } static Status ParseEntry(const llvm::StringRef &format_str, const FormatEntity::Entry::Definition *parent, FormatEntity::Entry &entry) { Status error; const size_t sep_pos = format_str.find_first_of(".[:"); const char sep_char = (sep_pos == llvm::StringRef::npos) ? '\0' : format_str[sep_pos]; llvm::StringRef key = format_str.substr(0, sep_pos); const size_t n = parent->num_children; for (size_t i = 0; i < n; ++i) { const FormatEntity::Entry::Definition *entry_def = parent->children + i; if (key.equals(entry_def->name) || entry_def->name[0] == '*') { llvm::StringRef value; if (sep_char) value = format_str.substr(sep_pos + (entry_def->keep_separator ? 0 : 1)); switch (entry_def->type) { case FormatEntity::Entry::Type::ParentString: entry.string = format_str.str(); return error; // Success case FormatEntity::Entry::Type::ParentNumber: entry.number = entry_def->data; return error; // Success case FormatEntity::Entry::Type::InsertString: entry.type = entry_def->type; entry.string = entry_def->string; return error; // Success default: entry.type = entry_def->type; break; } if (value.empty()) { if (entry_def->type == FormatEntity::Entry::Type::Invalid) { if (entry_def->children) { StreamString error_strm; error_strm.Printf("'%s' can't be specified on its own, you must " "access one of its children: ", entry_def->name); DumpCommaSeparatedChildEntryNames(error_strm, entry_def); error.SetErrorStringWithFormat("%s", error_strm.GetData()); } else if (sep_char == ':') { // Any value whose separator is a with a ':' means this value has a // string argument // that needs to be stored in the entry (like "${script.var:}"). // In this case the string value is the empty string which is ok. } else { error.SetErrorStringWithFormat("%s", "invalid entry definitions"); } } } else { if (entry_def->children) { error = ParseEntry(value, entry_def, entry); } else if (sep_char == ':') { // Any value whose separator is a with a ':' means this value has a // string argument // that needs to be stored in the entry (like // "${script.var:modulename.function}") entry.string = value.str(); } else { error.SetErrorStringWithFormat( "'%s' followed by '%s' but it has no children", key.str().c_str(), value.str().c_str()); } } return error; } } StreamString error_strm; if (parent->type == FormatEntity::Entry::Type::Root) error_strm.Printf( "invalid top level item '%s'. Valid top level items are: ", key.str().c_str()); else error_strm.Printf("invalid member '%s' in '%s'. Valid members are: ", key.str().c_str(), parent->name); DumpCommaSeparatedChildEntryNames(error_strm, parent); error.SetErrorStringWithFormat("%s", error_strm.GetData()); return error; } static const FormatEntity::Entry::Definition * FindEntry(const llvm::StringRef &format_str, const FormatEntity::Entry::Definition *parent, llvm::StringRef &remainder) { Status error; std::pair p = format_str.split('.'); const size_t n = parent->num_children; for (size_t i = 0; i < n; ++i) { const FormatEntity::Entry::Definition *entry_def = parent->children + i; if (p.first.equals(entry_def->name) || entry_def->name[0] == '*') { if (p.second.empty()) { if (format_str.back() == '.') remainder = format_str.drop_front(format_str.size() - 1); else remainder = llvm::StringRef(); // Exact match return entry_def; } else { if (entry_def->children) { return FindEntry(p.second, entry_def, remainder); } else { remainder = p.second; return entry_def; } } } } remainder = format_str; return parent; } Status FormatEntity::ParseInternal(llvm::StringRef &format, Entry &parent_entry, uint32_t depth) { Status error; while (!format.empty() && error.Success()) { const size_t non_special_chars = format.find_first_of("${}\\"); if (non_special_chars == llvm::StringRef::npos) { // No special characters, just string bytes so add them and we are done parent_entry.AppendText(format); return error; } if (non_special_chars > 0) { // We have a special character, so add all characters before these as a // plain string parent_entry.AppendText(format.substr(0, non_special_chars)); format = format.drop_front(non_special_chars); } switch (format[0]) { case '\0': return error; case '{': { format = format.drop_front(); // Skip the '{' Entry scope_entry(Entry::Type::Scope); error = FormatEntity::ParseInternal(format, scope_entry, depth + 1); if (error.Fail()) return error; parent_entry.AppendEntry(std::move(scope_entry)); } break; case '}': if (depth == 0) error.SetErrorString("unmatched '}' character"); else format = format .drop_front(); // Skip the '}' as we are at the end of the scope return error; case '\\': { format = format.drop_front(); // Skip the '\' character if (format.empty()) { error.SetErrorString( "'\\' character was not followed by another character"); return error; } const char desens_char = format[0]; format = format.drop_front(); // Skip the desensitized char character switch (desens_char) { case 'a': parent_entry.AppendChar('\a'); break; case 'b': parent_entry.AppendChar('\b'); break; case 'f': parent_entry.AppendChar('\f'); break; case 'n': parent_entry.AppendChar('\n'); break; case 'r': parent_entry.AppendChar('\r'); break; case 't': parent_entry.AppendChar('\t'); break; case 'v': parent_entry.AppendChar('\v'); break; case '\'': parent_entry.AppendChar('\''); break; case '\\': parent_entry.AppendChar('\\'); break; case '0': // 1 to 3 octal chars { // Make a string that can hold onto the initial zero char, // up to 3 octal digits, and a terminating NULL. char oct_str[5] = {0, 0, 0, 0, 0}; int i; for (i = 0; (format[i] >= '0' && format[i] <= '7') && i < 4; ++i) oct_str[i] = format[i]; // We don't want to consume the last octal character since // the main for loop will do this for us, so we advance p by // one less than i (even if i is zero) format = format.drop_front(i); unsigned long octal_value = ::strtoul(oct_str, nullptr, 8); if (octal_value <= UINT8_MAX) { parent_entry.AppendChar((char)octal_value); } else { error.SetErrorString("octal number is larger than a single byte"); return error; } } break; case 'x': // hex number in the format if (isxdigit(format[0])) { // Make a string that can hold onto two hex chars plus a // NULL terminator char hex_str[3] = {0, 0, 0}; hex_str[0] = format[0]; format = format.drop_front(); if (isxdigit(format[0])) { hex_str[1] = format[0]; format = format.drop_front(); } unsigned long hex_value = strtoul(hex_str, nullptr, 16); if (hex_value <= UINT8_MAX) { parent_entry.AppendChar((char)hex_value); } else { error.SetErrorString("hex number is larger than a single byte"); return error; } } else { parent_entry.AppendChar(desens_char); } break; default: // Just desensitize any other character by just printing what // came after the '\' parent_entry.AppendChar(desens_char); break; } } break; case '$': if (format.size() == 1) { // '$' at the end of a format string, just print the '$' parent_entry.AppendText("$"); } else { format = format.drop_front(); // Skip the '$' if (format[0] == '{') { format = format.drop_front(); // Skip the '{' llvm::StringRef variable, variable_format; error = FormatEntity::ExtractVariableInfo(format, variable, variable_format); if (error.Fail()) return error; bool verify_is_thread_id = false; Entry entry; if (!variable_format.empty()) { entry.printf_format = variable_format.str(); // If the format contains a '%' we are going to assume this is // a printf style format. So if you want to format your thread ID // using "0x%llx" you can use: // ${thread.id%0x%llx} // // If there is no '%' in the format, then it is assumed to be a // LLDB format name, or one of the extended formats specified in // the switch statement below. if (entry.printf_format.find('%') == std::string::npos) { bool clear_printf = false; if (FormatManager::GetFormatFromCString( entry.printf_format.c_str(), false, entry.fmt)) { // We have an LLDB format, so clear the printf format clear_printf = true; } else if (entry.printf_format.size() == 1) { switch (entry.printf_format[0]) { case '@': // if this is an @ sign, print ObjC description entry.number = ValueObject:: eValueObjectRepresentationStyleLanguageSpecific; clear_printf = true; break; case 'V': // if this is a V, print the value using the default // format entry.number = ValueObject::eValueObjectRepresentationStyleValue; clear_printf = true; break; case 'L': // if this is an L, print the location of the value entry.number = ValueObject::eValueObjectRepresentationStyleLocation; clear_printf = true; break; case 'S': // if this is an S, print the summary after all entry.number = ValueObject::eValueObjectRepresentationStyleSummary; clear_printf = true; break; case '#': // if this is a '#', print the number of children entry.number = ValueObject::eValueObjectRepresentationStyleChildrenCount; clear_printf = true; break; case 'T': // if this is a 'T', print the type entry.number = ValueObject::eValueObjectRepresentationStyleType; clear_printf = true; break; case 'N': // if this is a 'N', print the name entry.number = ValueObject::eValueObjectRepresentationStyleName; clear_printf = true; break; case '>': // if this is a '>', print the expression path entry.number = ValueObject:: eValueObjectRepresentationStyleExpressionPath; clear_printf = true; break; default: error.SetErrorStringWithFormat("invalid format: '%s'", entry.printf_format.c_str()); return error; } } else if (FormatManager::GetFormatFromCString( entry.printf_format.c_str(), true, entry.fmt)) { clear_printf = true; } else if (entry.printf_format == "tid") { verify_is_thread_id = true; } else { error.SetErrorStringWithFormat("invalid format: '%s'", entry.printf_format.c_str()); return error; } // Our format string turned out to not be a printf style format // so lets clear the string if (clear_printf) entry.printf_format.clear(); } } // Check for dereferences if (variable[0] == '*') { entry.deref = true; variable = variable.drop_front(); } error = ParseEntry(variable, &g_root, entry); if (error.Fail()) return error; if (verify_is_thread_id) { if (entry.type != Entry::Type::ThreadID && entry.type != Entry::Type::ThreadProtocolID) { error.SetErrorString("the 'tid' format can only be used on " "${thread.id} and ${thread.protocol_id}"); } } switch (entry.type) { case Entry::Type::Variable: case Entry::Type::VariableSynthetic: if (entry.number == 0) { if (entry.string.empty()) entry.number = ValueObject::eValueObjectRepresentationStyleValue; else entry.number = ValueObject::eValueObjectRepresentationStyleSummary; } break; default: // Make sure someone didn't try to dereference anything but ${var} // or ${svar} if (entry.deref) { error.SetErrorStringWithFormat( "${%s} can't be dereferenced, only ${var} and ${svar} can.", variable.str().c_str()); return error; } } // Check if this entry just wants to insert a constant string // value into the parent_entry, if so, insert the string with // AppendText, else append the entry to the parent_entry. if (entry.type == Entry::Type::InsertString) parent_entry.AppendText(entry.string.c_str()); else parent_entry.AppendEntry(std::move(entry)); } } break; } } return error; } Status FormatEntity::ExtractVariableInfo(llvm::StringRef &format_str, llvm::StringRef &variable_name, llvm::StringRef &variable_format) { Status error; variable_name = llvm::StringRef(); variable_format = llvm::StringRef(); const size_t paren_pos = format_str.find('}'); if (paren_pos != llvm::StringRef::npos) { const size_t percent_pos = format_str.find('%'); if (percent_pos < paren_pos) { if (percent_pos > 0) { if (percent_pos > 1) variable_name = format_str.substr(0, percent_pos); variable_format = format_str.substr(percent_pos + 1, paren_pos - (percent_pos + 1)); } } else { variable_name = format_str.substr(0, paren_pos); } // Strip off elements and the formatting and the trailing '}' format_str = format_str.substr(paren_pos + 1); } else { error.SetErrorStringWithFormat( "missing terminating '}' character for '${%s'", format_str.str().c_str()); } return error; } bool FormatEntity::FormatFileSpec(const FileSpec &file_spec, Stream &s, llvm::StringRef variable_name, llvm::StringRef variable_format) { if (variable_name.empty() || variable_name.equals(".fullpath")) { file_spec.Dump(&s); return true; } else if (variable_name.equals(".basename")) { s.PutCString(file_spec.GetFilename().AsCString("")); return true; } else if (variable_name.equals(".dirname")) { s.PutCString(file_spec.GetFilename().AsCString("")); return true; } return false; } static std::string MakeMatch(const llvm::StringRef &prefix, const char *suffix) { std::string match(prefix.str()); match.append(suffix); return match; } static void AddMatches(const FormatEntity::Entry::Definition *def, const llvm::StringRef &prefix, const llvm::StringRef &match_prefix, StringList &matches) { const size_t n = def->num_children; if (n > 0) { for (size_t i = 0; i < n; ++i) { std::string match = prefix.str(); if (match_prefix.empty()) matches.AppendString(MakeMatch(prefix, def->children[i].name)); else if (strncmp(def->children[i].name, match_prefix.data(), match_prefix.size()) == 0) matches.AppendString( MakeMatch(prefix, def->children[i].name + match_prefix.size())); } } } size_t FormatEntity::AutoComplete(llvm::StringRef str, int match_start_point, int max_return_elements, bool &word_complete, StringList &matches) { word_complete = false; str = str.drop_front(match_start_point); matches.Clear(); const size_t dollar_pos = str.rfind('$'); if (dollar_pos == llvm::StringRef::npos) return 0; // Hitting TAB after $ at the end of the string add a "{" if (dollar_pos == str.size() - 1) { std::string match = str.str(); match.append("{"); matches.AppendString(match); return 1; } if (str[dollar_pos + 1] != '{') return 0; const size_t close_pos = str.find('}', dollar_pos + 2); if (close_pos != llvm::StringRef::npos) return 0; const size_t format_pos = str.find('%', dollar_pos + 2); if (format_pos != llvm::StringRef::npos) return 0; llvm::StringRef partial_variable(str.substr(dollar_pos + 2)); if (partial_variable.empty()) { // Suggest all top level entites as we are just past "${" AddMatches(&g_root, str, llvm::StringRef(), matches); return matches.GetSize(); } // We have a partially specified variable, find it llvm::StringRef remainder; const FormatEntity::Entry::Definition *entry_def = FindEntry(partial_variable, &g_root, remainder); if (!entry_def) return 0; const size_t n = entry_def->num_children; if (remainder.empty()) { // Exact match if (n > 0) { // "${thread.info" matches.AppendString(MakeMatch(str, ".")); } else { // "${thread.id" matches.AppendString(MakeMatch(str, "}")); word_complete = true; } } else if (remainder.equals(".")) { // "${thread." AddMatches(entry_def, str, llvm::StringRef(), matches); } else { // We have a partial match // "${thre" AddMatches(entry_def, str, remainder, matches); } return matches.GetSize(); } Index: vendor/lldb/dist/source/Core/StructuredData.cpp =================================================================== --- vendor/lldb/dist/source/Core/StructuredData.cpp (revision 319149) +++ vendor/lldb/dist/source/Core/StructuredData.cpp (revision 319150) @@ -1,314 +1,314 @@ //===---------------------StructuredData.cpp ---------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Core/StructuredData.h" #include "lldb/Host/File.h" #include "lldb/Host/StringConvert.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/JSON.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/Stream.h" // for Stream #include "lldb/Utility/StreamString.h" #include "lldb/lldb-enumerations.h" // for FilePermissions::eFilePermiss... #include "lldb/lldb-forward.h" // for DataBufferSP #include "llvm/ADT/STLExtras.h" // for make_unique #include // for numeric_limits #include #include #include // for printf #include #include // for off_t using namespace lldb_private; //---------------------------------------------------------------------- // Functions that use a JSONParser to parse JSON into StructuredData //---------------------------------------------------------------------- static StructuredData::ObjectSP ParseJSONValue(JSONParser &json_parser); static StructuredData::ObjectSP ParseJSONObject(JSONParser &json_parser); static StructuredData::ObjectSP ParseJSONArray(JSONParser &json_parser); StructuredData::ObjectSP StructuredData::ParseJSONFromFile(const FileSpec &input_spec, Status &error) { StructuredData::ObjectSP return_sp; if (!input_spec.Exists()) { error.SetErrorStringWithFormat("input file %s does not exist.", input_spec.GetPath().c_str()); return return_sp; } File input_file(nullptr, File::OpenOptions::eOpenOptionRead, lldb::eFilePermissionsUserRead); std::string input_path = input_spec.GetPath(); error = input_file.Open(input_path.c_str(), File::OpenOptions::eOpenOptionRead, lldb::eFilePermissionsUserRead); if (!error.Success()) { error.SetErrorStringWithFormat("could not open input file: %s - %s.", input_spec.GetPath().c_str(), error.AsCString()); return return_sp; } lldb::DataBufferSP input_data; size_t num_bytes = std::numeric_limits::max(); off_t offset = 0; error = input_file.Read(num_bytes, offset, true, input_data); if (!error.Success()) { error.SetErrorStringWithFormat("could not read input file: %s - %s.", input_spec.GetPath().c_str(), error.AsCString()); return return_sp; } JSONParser json_parser((char *)input_data->GetBytes()); return_sp = ParseJSONValue(json_parser); return return_sp; } static StructuredData::ObjectSP ParseJSONObject(JSONParser &json_parser) { // The "JSONParser::Token::ObjectStart" token should have already been // consumed by the time this function is called auto dict_up = llvm::make_unique(); std::string value; std::string key; while (1) { JSONParser::Token token = json_parser.GetToken(value); if (token == JSONParser::Token::String) { key.swap(value); token = json_parser.GetToken(value); if (token == JSONParser::Token::Colon) { StructuredData::ObjectSP value_sp = ParseJSONValue(json_parser); if (value_sp) dict_up->AddItem(key, value_sp); else break; } } else if (token == JSONParser::Token::ObjectEnd) { return StructuredData::ObjectSP(dict_up.release()); } else if (token == JSONParser::Token::Comma) { continue; } else { break; } } return StructuredData::ObjectSP(); } static StructuredData::ObjectSP ParseJSONArray(JSONParser &json_parser) { // The "JSONParser::Token::ObjectStart" token should have already been // consumed // by the time this function is called auto array_up = llvm::make_unique(); std::string value; std::string key; while (1) { StructuredData::ObjectSP value_sp = ParseJSONValue(json_parser); if (value_sp) array_up->AddItem(value_sp); else break; JSONParser::Token token = json_parser.GetToken(value); if (token == JSONParser::Token::Comma) { continue; } else if (token == JSONParser::Token::ArrayEnd) { return StructuredData::ObjectSP(array_up.release()); } else { break; } } return StructuredData::ObjectSP(); } static StructuredData::ObjectSP ParseJSONValue(JSONParser &json_parser) { std::string value; const JSONParser::Token token = json_parser.GetToken(value); switch (token) { case JSONParser::Token::ObjectStart: return ParseJSONObject(json_parser); case JSONParser::Token::ArrayStart: return ParseJSONArray(json_parser); case JSONParser::Token::Integer: { bool success = false; uint64_t uval = StringConvert::ToUInt64(value.c_str(), 0, 0, &success); if (success) return std::make_shared(uval); } break; case JSONParser::Token::Float: { bool success = false; double val = StringConvert::ToDouble(value.c_str(), 0.0, &success); if (success) return std::make_shared(val); } break; case JSONParser::Token::String: return std::make_shared(value); case JSONParser::Token::True: case JSONParser::Token::False: return std::make_shared(token == JSONParser::Token::True); case JSONParser::Token::Null: return std::make_shared(); default: break; } return StructuredData::ObjectSP(); } StructuredData::ObjectSP StructuredData::ParseJSON(std::string json_text) { JSONParser json_parser(json_text.c_str()); StructuredData::ObjectSP object_sp = ParseJSONValue(json_parser); return object_sp; } StructuredData::ObjectSP StructuredData::Object::GetObjectForDotSeparatedPath(llvm::StringRef path) { - if (this->GetType() == Type::eTypeDictionary) { + if (this->GetType() == lldb::eStructuredDataTypeDictionary) { std::pair match = path.split('.'); std::string key = match.first.str(); ObjectSP value = this->GetAsDictionary()->GetValueForKey(key); if (value.get()) { // Do we have additional words to descend? If not, return the // value we're at right now. if (match.second.empty()) { return value; } else { return value->GetObjectForDotSeparatedPath(match.second); } } return ObjectSP(); } - if (this->GetType() == Type::eTypeArray) { + if (this->GetType() == lldb::eStructuredDataTypeArray) { std::pair match = path.split('['); if (match.second.size() == 0) { return this->shared_from_this(); } errno = 0; uint64_t val = strtoul(match.second.str().c_str(), NULL, 10); if (errno == 0) { return this->GetAsArray()->GetItemAtIndex(val); } return ObjectSP(); } return this->shared_from_this(); } void StructuredData::Object::DumpToStdout(bool pretty_print) const { StreamString stream; Dump(stream, pretty_print); printf("%s\n", stream.GetData()); } void StructuredData::Array::Dump(Stream &s, bool pretty_print) const { bool first = true; s << "["; if (pretty_print) { s << "\n"; s.IndentMore(); } for (const auto &item_sp : m_items) { if (first) { first = false; } else { s << ","; if (pretty_print) s << "\n"; } if (pretty_print) s.Indent(); item_sp->Dump(s, pretty_print); } if (pretty_print) { s.IndentLess(); s.EOL(); s.Indent(); } s << "]"; } void StructuredData::Integer::Dump(Stream &s, bool pretty_print) const { s.Printf("%" PRIu64, m_value); } void StructuredData::Float::Dump(Stream &s, bool pretty_print) const { s.Printf("%lg", m_value); } void StructuredData::Boolean::Dump(Stream &s, bool pretty_print) const { if (m_value == true) s.PutCString("true"); else s.PutCString("false"); } void StructuredData::String::Dump(Stream &s, bool pretty_print) const { std::string quoted; const size_t strsize = m_value.size(); for (size_t i = 0; i < strsize; ++i) { char ch = m_value[i]; if (ch == '"' || ch == '\\') quoted.push_back('\\'); quoted.push_back(ch); } s.Printf("\"%s\"", quoted.c_str()); } void StructuredData::Dictionary::Dump(Stream &s, bool pretty_print) const { bool first = true; s << "{"; if (pretty_print) { s << "\n"; s.IndentMore(); } for (const auto &pair : m_dict) { if (first) first = false; else { s << ","; if (pretty_print) s << "\n"; } if (pretty_print) s.Indent(); s << "\"" << pair.first.AsCString() << "\" : "; pair.second->Dump(s, pretty_print); } if (pretty_print) { s.IndentLess(); s.EOL(); s.Indent(); } s << "}"; } void StructuredData::Null::Dump(Stream &s, bool pretty_print) const { s << "null"; } void StructuredData::Generic::Dump(Stream &s, bool pretty_print) const { s << "0x" << m_object; } Index: vendor/lldb/dist/source/Host/common/Editline.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/Editline.cpp (revision 319149) +++ vendor/lldb/dist/source/Host/common/Editline.cpp (revision 319150) @@ -1,1397 +1,1397 @@ //===-- Editline.cpp --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include #include #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Editline.h" #include "lldb/Host/Host.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/SelectHelper.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/StringList.h" #include "lldb/Utility/Timeout.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Threading.h" using namespace lldb_private; using namespace lldb_private::line_editor; // Workaround for what looks like an OS X-specific issue, but other platforms // may benefit from something similar if issues arise. The libedit library // doesn't explicitly initialize the curses termcap library, which it gets away // with until TERM is set to VT100 where it stumbles over an implementation // assumption that may not exist on other platforms. The setupterm() function // would normally require headers that don't work gracefully in this context, so // the function declaraction has been hoisted here. #if defined(__APPLE__) extern "C" { int setupterm(char *term, int fildes, int *errret); } #define USE_SETUPTERM_WORKAROUND #endif // Editline uses careful cursor management to achieve the illusion of editing a // multi-line block of text // with a single line editor. Preserving this illusion requires fairly careful // management of cursor // state. Read and understand the relationship between DisplayInput(), // MoveCursor(), SetCurrentLine(), // and SaveEditedLine() before making changes. #define ESCAPE "\x1b" #define ANSI_FAINT ESCAPE "[2m" #define ANSI_UNFAINT ESCAPE "[22m" #define ANSI_CLEAR_BELOW ESCAPE "[J" #define ANSI_CLEAR_RIGHT ESCAPE "[K" #define ANSI_SET_COLUMN_N ESCAPE "[%dG" #define ANSI_UP_N_ROWS ESCAPE "[%dA" #define ANSI_DOWN_N_ROWS ESCAPE "[%dB" #if LLDB_EDITLINE_USE_WCHAR #define EditLineConstString(str) L##str #define EditLineStringFormatSpec "%ls" #else #define EditLineConstString(str) str #define EditLineStringFormatSpec "%s" // use #defines so wide version functions and structs will resolve to old // versions // for case of libedit not built with wide char support #define history_w history #define history_winit history_init #define history_wend history_end #define HistoryW History #define HistEventW HistEvent #define LineInfoW LineInfo #define el_wgets el_gets #define el_wgetc el_getc #define el_wpush el_push #define el_wparse el_parse #define el_wset el_set #define el_wget el_get #define el_wline el_line #define el_winsertstr el_insertstr #define el_wdeletestr el_deletestr #endif // #if LLDB_EDITLINE_USE_WCHAR bool IsOnlySpaces(const EditLineStringType &content) { for (wchar_t ch : content) { if (ch != EditLineCharType(' ')) return false; } return true; } EditLineStringType CombineLines(const std::vector &lines) { EditLineStringStreamType combined_stream; for (EditLineStringType line : lines) { combined_stream << line.c_str() << "\n"; } return combined_stream.str(); } std::vector SplitLines(const EditLineStringType &input) { std::vector result; size_t start = 0; while (start < input.length()) { size_t end = input.find('\n', start); if (end == std::string::npos) { result.insert(result.end(), input.substr(start)); break; } result.insert(result.end(), input.substr(start, end - start)); start = end + 1; } return result; } EditLineStringType FixIndentation(const EditLineStringType &line, int indent_correction) { if (indent_correction == 0) return line; if (indent_correction < 0) return line.substr(-indent_correction); return EditLineStringType(indent_correction, EditLineCharType(' ')) + line; } int GetIndentation(const EditLineStringType &line) { int space_count = 0; for (EditLineCharType ch : line) { if (ch != EditLineCharType(' ')) break; ++space_count; } return space_count; } bool IsInputPending(FILE *file) { // FIXME: This will be broken on Windows if we ever re-enable Editline. You // can't use select // on something that isn't a socket. This will have to be re-written to not // use a FILE*, but // instead use some kind of yet-to-be-created abstraction that select-like // functionality on // non-socket objects. const int fd = fileno(file); SelectHelper select_helper; select_helper.SetTimeout(std::chrono::microseconds(0)); select_helper.FDSetRead(fd); return select_helper.Select().Success(); } namespace lldb_private { namespace line_editor { typedef std::weak_ptr EditlineHistoryWP; // EditlineHistory objects are sometimes shared between multiple // Editline instances with the same program name. class EditlineHistory { private: // Use static GetHistory() function to get a EditlineHistorySP to one of these // objects EditlineHistory(const std::string &prefix, uint32_t size, bool unique_entries) : m_history(NULL), m_event(), m_prefix(prefix), m_path() { m_history = history_winit(); history_w(m_history, &m_event, H_SETSIZE, size); if (unique_entries) history_w(m_history, &m_event, H_SETUNIQUE, 1); } const char *GetHistoryFilePath() { if (m_path.empty() && m_history && !m_prefix.empty()) { FileSpec parent_path{"~/.lldb", true}; char history_path[PATH_MAX]; if (!llvm::sys::fs::create_directory(parent_path.GetPath())) { snprintf(history_path, sizeof(history_path), "~/.lldb/%s-history", m_prefix.c_str()); } else { snprintf(history_path, sizeof(history_path), "~/%s-widehistory", m_prefix.c_str()); } m_path = FileSpec(history_path, true).GetPath(); } if (m_path.empty()) return NULL; return m_path.c_str(); } public: ~EditlineHistory() { Save(); if (m_history) { history_wend(m_history); m_history = NULL; } } static EditlineHistorySP GetHistory(const std::string &prefix) { typedef std::map WeakHistoryMap; static std::recursive_mutex g_mutex; static WeakHistoryMap g_weak_map; std::lock_guard guard(g_mutex); WeakHistoryMap::const_iterator pos = g_weak_map.find(prefix); EditlineHistorySP history_sp; if (pos != g_weak_map.end()) { history_sp = pos->second.lock(); if (history_sp) return history_sp; g_weak_map.erase(pos); } history_sp.reset(new EditlineHistory(prefix, 800, true)); g_weak_map[prefix] = history_sp; return history_sp; } bool IsValid() const { return m_history != NULL; } HistoryW *GetHistoryPtr() { return m_history; } void Enter(const EditLineCharType *line_cstr) { if (m_history) history_w(m_history, &m_event, H_ENTER, line_cstr); } bool Load() { if (m_history) { const char *path = GetHistoryFilePath(); if (path) { history_w(m_history, &m_event, H_LOAD, path); return true; } } return false; } bool Save() { if (m_history) { const char *path = GetHistoryFilePath(); if (path) { history_w(m_history, &m_event, H_SAVE, path); return true; } } return false; } protected: HistoryW *m_history; // The history object HistEventW m_event; // The history event needed to contain all history events std::string m_prefix; // The prefix name (usually the editline program name) // to use when loading/saving history std::string m_path; // Path to the history file }; } } //------------------------------------------------------------------ // Editline private methods //------------------------------------------------------------------ void Editline::SetBaseLineNumber(int line_number) { std::stringstream line_number_stream; line_number_stream << line_number; m_base_line_number = line_number; m_line_number_digits = std::max(3, (int)line_number_stream.str().length() + 1); } std::string Editline::PromptForIndex(int line_index) { bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0; std::string prompt = m_set_prompt; if (use_line_numbers && prompt.length() == 0) { prompt = ": "; } std::string continuation_prompt = prompt; if (m_set_continuation_prompt.length() > 0) { continuation_prompt = m_set_continuation_prompt; // Ensure that both prompts are the same length through space padding while (continuation_prompt.length() < prompt.length()) { continuation_prompt += ' '; } while (prompt.length() < continuation_prompt.length()) { prompt += ' '; } } if (use_line_numbers) { StreamString prompt_stream; prompt_stream.Printf( "%*d%s", m_line_number_digits, m_base_line_number + line_index, (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str()); return std::move(prompt_stream.GetString()); } return (line_index == 0) ? prompt : continuation_prompt; } void Editline::SetCurrentLine(int line_index) { m_current_line_index = line_index; m_current_prompt = PromptForIndex(line_index); } int Editline::GetPromptWidth() { return (int)PromptForIndex(0).length(); } bool Editline::IsEmacs() { const char *editor; el_get(m_editline, EL_EDITOR, &editor); return editor[0] == 'e'; } bool Editline::IsOnlySpaces() { const LineInfoW *info = el_wline(m_editline); for (const EditLineCharType *character = info->buffer; character < info->lastchar; character++) { if (*character != ' ') return false; } return true; } int Editline::GetLineIndexForLocation(CursorLocation location, int cursor_row) { int line = 0; if (location == CursorLocation::EditingPrompt || location == CursorLocation::BlockEnd || location == CursorLocation::EditingCursor) { for (unsigned index = 0; index < m_current_line_index; index++) { line += CountRowsForLine(m_input_lines[index]); } if (location == CursorLocation::EditingCursor) { line += cursor_row; } else if (location == CursorLocation::BlockEnd) { for (unsigned index = m_current_line_index; index < m_input_lines.size(); index++) { line += CountRowsForLine(m_input_lines[index]); } --line; } } return line; } void Editline::MoveCursor(CursorLocation from, CursorLocation to) { const LineInfoW *info = el_wline(m_editline); int editline_cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth()); int editline_cursor_row = editline_cursor_position / m_terminal_width; // Determine relative starting and ending lines int fromLine = GetLineIndexForLocation(from, editline_cursor_row); int toLine = GetLineIndexForLocation(to, editline_cursor_row); if (toLine != fromLine) { fprintf(m_output_file, (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS, std::abs(toLine - fromLine)); } // Determine target column int toColumn = 1; if (to == CursorLocation::EditingCursor) { toColumn = editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1; } else if (to == CursorLocation::BlockEnd && !m_input_lines.empty()) { toColumn = ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) % 80) + 1; } fprintf(m_output_file, ANSI_SET_COLUMN_N, toColumn); } void Editline::DisplayInput(int firstIndex) { fprintf(m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1); int line_count = (int)m_input_lines.size(); const char *faint = m_color_prompts ? ANSI_FAINT : ""; const char *unfaint = m_color_prompts ? ANSI_UNFAINT : ""; for (int index = firstIndex; index < line_count; index++) { fprintf(m_output_file, "%s" "%s" "%s" EditLineStringFormatSpec " ", faint, PromptForIndex(index).c_str(), unfaint, m_input_lines[index].c_str()); if (index < line_count - 1) fprintf(m_output_file, "\n"); } } int Editline::CountRowsForLine(const EditLineStringType &content) { auto prompt = PromptForIndex(0); // Prompt width is constant during an edit session int line_length = (int)(content.length() + prompt.length()); return (line_length / m_terminal_width) + 1; } void Editline::SaveEditedLine() { const LineInfoW *info = el_wline(m_editline); m_input_lines[m_current_line_index] = EditLineStringType(info->buffer, info->lastchar - info->buffer); } StringList Editline::GetInputAsStringList(int line_count) { StringList lines; for (EditLineStringType line : m_input_lines) { if (line_count == 0) break; #if LLDB_EDITLINE_USE_WCHAR lines.AppendString(m_utf8conv.to_bytes(line)); #else lines.AppendString(line); #endif --line_count; } return lines; } unsigned char Editline::RecallHistory(bool earlier) { if (!m_history_sp || !m_history_sp->IsValid()) return CC_ERROR; HistoryW *pHistory = m_history_sp->GetHistoryPtr(); HistEventW history_event; std::vector new_input_lines; // Treat moving from the "live" entry differently if (!m_in_history) { if (earlier == false) return CC_ERROR; // Can't go newer than the "live" entry if (history_w(pHistory, &history_event, H_FIRST) == -1) return CC_ERROR; // Save any edits to the "live" entry in case we return by moving forward in // history // (it would be more bash-like to save over any current entry, but libedit // doesn't // offer the ability to add entries anywhere except the end.) SaveEditedLine(); m_live_history_lines = m_input_lines; m_in_history = true; } else { if (history_w(pHistory, &history_event, earlier ? H_NEXT : H_PREV) == -1) { // Can't move earlier than the earliest entry if (earlier) return CC_ERROR; // ... but moving to newer than the newest yields the "live" entry new_input_lines = m_live_history_lines; m_in_history = false; } } // If we're pulling the lines from history, split them apart if (m_in_history) new_input_lines = SplitLines(history_event.str); // Erase the current edit session and replace it with a new one MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); m_input_lines = new_input_lines; DisplayInput(); // Prepare to edit the last line when moving to previous entry, or the first // line // when moving to next entry SetCurrentLine(m_current_line_index = earlier ? (int)m_input_lines.size() - 1 : 0); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); return CC_NEWLINE; } -int Editline::GetCharacter(EditLineCharType *c) { +int Editline::GetCharacter(EditLineGetCharType *c) { const LineInfoW *info = el_wline(m_editline); // Paint a faint version of the desired prompt over the version libedit draws // (will only be requested if colors are supported) if (m_needs_prompt_repaint) { MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); fprintf(m_output_file, "%s" "%s" "%s", ANSI_FAINT, Prompt(), ANSI_UNFAINT); MoveCursor(CursorLocation::EditingPrompt, CursorLocation::EditingCursor); m_needs_prompt_repaint = false; } if (m_multiline_enabled) { // Detect when the number of rows used for this input line changes due to an // edit int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth()); int new_line_rows = (lineLength / m_terminal_width) + 1; if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows) { // Respond by repainting the current state from this line on MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); SaveEditedLine(); DisplayInput(m_current_line_index); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); } m_current_line_rows = new_line_rows; } // Read an actual character while (true) { lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess; char ch = 0; // This mutex is locked by our caller (GetLine). Unlock it while we read a // character // (blocking operation), so we do not hold the mutex indefinitely. This // gives a chance // for someone to interrupt us. After Read returns, immediately lock the // mutex again and // check if we were interrupted. m_output_mutex.unlock(); int read_count = m_input_connection.Read(&ch, 1, llvm::None, status, NULL); m_output_mutex.lock(); if (m_editor_status == EditorStatus::Interrupted) { while (read_count > 0 && status == lldb::eConnectionStatusSuccess) read_count = m_input_connection.Read(&ch, 1, llvm::None, status, NULL); lldbassert(status == lldb::eConnectionStatusInterrupted); return 0; } if (read_count) { if (CompleteCharacter(ch, *c)) return 1; } else { switch (status) { case lldb::eConnectionStatusSuccess: // Success break; case lldb::eConnectionStatusInterrupted: lldbassert(0 && "Interrupts should have been handled above."); case lldb::eConnectionStatusError: // Check GetError() for details case lldb::eConnectionStatusTimedOut: // Request timed out case lldb::eConnectionStatusEndOfFile: // End-of-file encountered case lldb::eConnectionStatusNoConnection: // No connection case lldb::eConnectionStatusLostConnection: // Lost connection while // connected to a valid // connection m_editor_status = EditorStatus::EndOfInput; return 0; } } } } const char *Editline::Prompt() { if (m_color_prompts) m_needs_prompt_repaint = true; return m_current_prompt.c_str(); } unsigned char Editline::BreakLineCommand(int ch) { // Preserve any content beyond the cursor, truncate and save the current line const LineInfoW *info = el_wline(m_editline); auto current_line = EditLineStringType(info->buffer, info->cursor - info->buffer); auto new_line_fragment = EditLineStringType(info->cursor, info->lastchar - info->cursor); m_input_lines[m_current_line_index] = current_line; // Ignore whitespace-only extra fragments when breaking a line if (::IsOnlySpaces(new_line_fragment)) new_line_fragment = EditLineConstString(""); // Establish the new cursor position at the start of a line when inserting a // line break m_revert_cursor_index = 0; // Don't perform automatic formatting when pasting if (!IsInputPending(m_input_file)) { // Apply smart indentation if (m_fix_indentation_callback) { StringList lines = GetInputAsStringList(m_current_line_index + 1); #if LLDB_EDITLINE_USE_WCHAR lines.AppendString(m_utf8conv.to_bytes(new_line_fragment)); #else lines.AppendString(new_line_fragment); #endif int indent_correction = m_fix_indentation_callback( this, lines, 0, m_fix_indentation_callback_baton); new_line_fragment = FixIndentation(new_line_fragment, indent_correction); m_revert_cursor_index = GetIndentation(new_line_fragment); } } // Insert the new line and repaint everything from the split line on down m_input_lines.insert(m_input_lines.begin() + m_current_line_index + 1, new_line_fragment); MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); DisplayInput(m_current_line_index); // Reposition the cursor to the right line and prepare to edit the new line SetCurrentLine(m_current_line_index + 1); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); return CC_NEWLINE; } unsigned char Editline::EndOrAddLineCommand(int ch) { // Don't perform end of input detection when pasting, always treat this as a // line break if (IsInputPending(m_input_file)) { return BreakLineCommand(ch); } // Save any edits to this line SaveEditedLine(); // If this is the end of the last line, consider whether to add a line instead const LineInfoW *info = el_wline(m_editline); if (m_current_line_index == m_input_lines.size() - 1 && info->cursor == info->lastchar) { if (m_is_input_complete_callback) { auto lines = GetInputAsStringList(); if (!m_is_input_complete_callback(this, lines, m_is_input_complete_callback_baton)) { return BreakLineCommand(ch); } // The completion test is allowed to change the input lines when complete m_input_lines.clear(); for (unsigned index = 0; index < lines.GetSize(); index++) { #if LLDB_EDITLINE_USE_WCHAR m_input_lines.insert(m_input_lines.end(), m_utf8conv.from_bytes(lines[index])); #else m_input_lines.insert(m_input_lines.end(), lines[index]); #endif } } } MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd); fprintf(m_output_file, "\n"); m_editor_status = EditorStatus::Complete; return CC_NEWLINE; } unsigned char Editline::DeleteNextCharCommand(int ch) { LineInfoW *info = const_cast(el_wline(m_editline)); // Just delete the next character normally if possible if (info->cursor < info->lastchar) { info->cursor++; el_deletestr(m_editline, 1); return CC_REFRESH; } // Fail when at the end of the last line, except when ^D is pressed on // the line is empty, in which case it is treated as EOF if (m_current_line_index == m_input_lines.size() - 1) { if (ch == 4 && info->buffer == info->lastchar) { fprintf(m_output_file, "^D\n"); m_editor_status = EditorStatus::EndOfInput; return CC_EOF; } return CC_ERROR; } // Prepare to combine this line with the one below MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); // Insert the next line of text at the cursor and restore the cursor position const EditLineCharType *cursor = info->cursor; el_winsertstr(m_editline, m_input_lines[m_current_line_index + 1].c_str()); info->cursor = cursor; SaveEditedLine(); // Delete the extra line m_input_lines.erase(m_input_lines.begin() + m_current_line_index + 1); // Clear and repaint from this line on down DisplayInput(m_current_line_index); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); return CC_REFRESH; } unsigned char Editline::DeletePreviousCharCommand(int ch) { LineInfoW *info = const_cast(el_wline(m_editline)); // Just delete the previous character normally when not at the start of a line if (info->cursor > info->buffer) { el_deletestr(m_editline, 1); return CC_REFRESH; } // No prior line and no prior character? Let the user know if (m_current_line_index == 0) return CC_ERROR; // No prior character, but prior line? Combine with the line above SaveEditedLine(); SetCurrentLine(m_current_line_index - 1); auto priorLine = m_input_lines[m_current_line_index]; m_input_lines.erase(m_input_lines.begin() + m_current_line_index); m_input_lines[m_current_line_index] = priorLine + m_input_lines[m_current_line_index]; // Repaint from the new line down fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, CountRowsForLine(priorLine), 1); DisplayInput(m_current_line_index); // Put the cursor back where libedit expects it to be before returning to // editing // by telling libedit about the newly inserted text MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); el_winsertstr(m_editline, priorLine.c_str()); return CC_REDISPLAY; } unsigned char Editline::PreviousLineCommand(int ch) { SaveEditedLine(); if (m_current_line_index == 0) { return RecallHistory(true); } // Start from a known location MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); // Treat moving up from a blank last line as a deletion of that line if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) { m_input_lines.erase(m_input_lines.begin() + m_current_line_index); fprintf(m_output_file, ANSI_CLEAR_BELOW); } SetCurrentLine(m_current_line_index - 1); fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, CountRowsForLine(m_input_lines[m_current_line_index]), 1); return CC_NEWLINE; } unsigned char Editline::NextLineCommand(int ch) { SaveEditedLine(); // Handle attempts to move down from the last line if (m_current_line_index == m_input_lines.size() - 1) { // Don't add an extra line if the existing last line is blank, move through // history instead if (IsOnlySpaces()) { return RecallHistory(false); } // Determine indentation for the new line int indentation = 0; if (m_fix_indentation_callback) { StringList lines = GetInputAsStringList(); lines.AppendString(""); indentation = m_fix_indentation_callback( this, lines, 0, m_fix_indentation_callback_baton); } m_input_lines.insert( m_input_lines.end(), EditLineStringType(indentation, EditLineCharType(' '))); } // Move down past the current line using newlines to force scrolling if needed SetCurrentLine(m_current_line_index + 1); const LineInfoW *info = el_wline(m_editline); int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth()); int cursor_row = cursor_position / m_terminal_width; for (int line_count = 0; line_count < m_current_line_rows - cursor_row; line_count++) { fprintf(m_output_file, "\n"); } return CC_NEWLINE; } unsigned char Editline::PreviousHistoryCommand(int ch) { SaveEditedLine(); return RecallHistory(true); } unsigned char Editline::NextHistoryCommand(int ch) { SaveEditedLine(); return RecallHistory(false); } unsigned char Editline::FixIndentationCommand(int ch) { if (!m_fix_indentation_callback) return CC_NORM; // Insert the character typed before proceeding EditLineCharType inserted[] = {(EditLineCharType)ch, 0}; el_winsertstr(m_editline, inserted); LineInfoW *info = const_cast(el_wline(m_editline)); int cursor_position = info->cursor - info->buffer; // Save the edits and determine the correct indentation level SaveEditedLine(); StringList lines = GetInputAsStringList(m_current_line_index + 1); int indent_correction = m_fix_indentation_callback( this, lines, cursor_position, m_fix_indentation_callback_baton); // If it is already correct no special work is needed if (indent_correction == 0) return CC_REFRESH; // Change the indentation level of the line std::string currentLine = lines.GetStringAtIndex(m_current_line_index); if (indent_correction > 0) { currentLine = currentLine.insert(0, indent_correction, ' '); } else { currentLine = currentLine.erase(0, -indent_correction); } #if LLDB_EDITLINE_USE_WCHAR m_input_lines[m_current_line_index] = m_utf8conv.from_bytes(currentLine); #else m_input_lines[m_current_line_index] = currentLine; #endif // Update the display to reflect the change MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); DisplayInput(m_current_line_index); // Reposition the cursor back on the original line and prepare to restart // editing // with a new cursor position SetCurrentLine(m_current_line_index); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); m_revert_cursor_index = cursor_position + indent_correction; return CC_NEWLINE; } unsigned char Editline::RevertLineCommand(int ch) { el_winsertstr(m_editline, m_input_lines[m_current_line_index].c_str()); if (m_revert_cursor_index >= 0) { LineInfoW *info = const_cast(el_wline(m_editline)); info->cursor = info->buffer + m_revert_cursor_index; if (info->cursor > info->lastchar) { info->cursor = info->lastchar; } m_revert_cursor_index = -1; } return CC_REFRESH; } unsigned char Editline::BufferStartCommand(int ch) { SaveEditedLine(); MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); SetCurrentLine(0); m_revert_cursor_index = 0; return CC_NEWLINE; } unsigned char Editline::BufferEndCommand(int ch) { SaveEditedLine(); MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd); SetCurrentLine((int)m_input_lines.size() - 1); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); return CC_NEWLINE; } unsigned char Editline::TabCommand(int ch) { if (m_completion_callback == nullptr) return CC_ERROR; const LineInfo *line_info = el_line(m_editline); StringList completions; int page_size = 40; const int num_completions = m_completion_callback( line_info->buffer, line_info->cursor, line_info->lastchar, 0, // Don't skip any matches (start at match zero) -1, // Get all the matches completions, m_completion_callback_baton); if (num_completions == 0) return CC_ERROR; // if (num_completions == -1) // { // el_insertstr (m_editline, m_completion_key); // return CC_REDISPLAY; // } // else if (num_completions == -2) { // Replace the entire line with the first string... el_deletestr(m_editline, line_info->cursor - line_info->buffer); el_insertstr(m_editline, completions.GetStringAtIndex(0)); return CC_REDISPLAY; } // If we get a longer match display that first. const char *completion_str = completions.GetStringAtIndex(0); if (completion_str != nullptr && *completion_str != '\0') { el_insertstr(m_editline, completion_str); return CC_REDISPLAY; } if (num_completions > 1) { int num_elements = num_completions + 1; fprintf(m_output_file, "\n" ANSI_CLEAR_BELOW "Available completions:"); if (num_completions < page_size) { for (int i = 1; i < num_elements; i++) { completion_str = completions.GetStringAtIndex(i); fprintf(m_output_file, "\n\t%s", completion_str); } fprintf(m_output_file, "\n"); } else { int cur_pos = 1; char reply; int got_char; while (cur_pos < num_elements) { int endpoint = cur_pos + page_size; if (endpoint > num_elements) endpoint = num_elements; for (; cur_pos < endpoint; cur_pos++) { completion_str = completions.GetStringAtIndex(cur_pos); fprintf(m_output_file, "\n\t%s", completion_str); } if (cur_pos >= num_elements) { fprintf(m_output_file, "\n"); break; } fprintf(m_output_file, "\nMore (Y/n/a): "); reply = 'n'; got_char = el_getc(m_editline, &reply); if (got_char == -1 || reply == 'n') break; if (reply == 'a') page_size = num_elements - cur_pos; } } DisplayInput(); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); } return CC_REDISPLAY; } void Editline::ConfigureEditor(bool multiline) { if (m_editline && m_multiline_enabled == multiline) return; m_multiline_enabled = multiline; if (m_editline) { // Disable edit mode to stop the terminal from flushing all input // during the call to el_end() since we expect to have multiple editline // instances in this program. el_set(m_editline, EL_EDITMODE, 0); el_end(m_editline); } m_editline = el_init(m_editor_name.c_str(), m_input_file, m_output_file, m_error_file); TerminalSizeChanged(); if (m_history_sp && m_history_sp->IsValid()) { m_history_sp->Load(); el_wset(m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr()); } el_set(m_editline, EL_CLIENTDATA, this); el_set(m_editline, EL_SIGNAL, 0); el_set(m_editline, EL_EDITOR, "emacs"); el_set(m_editline, EL_PROMPT, (EditlinePromptCallbackType)([](EditLine *editline) { return Editline::InstanceFor(editline)->Prompt(); })); el_wset(m_editline, EL_GETCFN, (EditlineGetCharCallbackType)([]( - EditLine *editline, EditLineCharType *c) { + EditLine *editline, EditLineGetCharType *c) { return Editline::InstanceFor(editline)->GetCharacter(c); })); // Commands used for multiline support, registered whether or not they're used el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-break-line"), EditLineConstString("Insert a line break"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BreakLineCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-end-or-add-line"), EditLineConstString("End editing or continue when incomplete"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->EndOrAddLineCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-delete-next-char"), EditLineConstString("Delete next character"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch); })); el_wset( m_editline, EL_ADDFN, EditLineConstString("lldb-delete-previous-char"), EditLineConstString("Delete previous character"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->DeletePreviousCharCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-line"), EditLineConstString("Move to previous line"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->PreviousLineCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-line"), EditLineConstString("Move to next line"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->NextLineCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-history"), EditLineConstString("Move to previous history"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->PreviousHistoryCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-history"), EditLineConstString("Move to next history"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->NextHistoryCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-start"), EditLineConstString("Move to start of buffer"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BufferStartCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-end"), EditLineConstString("Move to end of buffer"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BufferEndCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-fix-indentation"), EditLineConstString("Fix line indentation"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->FixIndentationCommand(ch); })); // Register the complete callback under two names for compatibility with older // clients using // custom .editrc files (largely because libedit has a bad bug where if you // have a bind command // that tries to bind to a function name that doesn't exist, it can corrupt // the heap and // crash your process later.) EditlineCommandCallbackType complete_callback = [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->TabCommand(ch); }; el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-complete"), EditLineConstString("Invoke completion"), complete_callback); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb_complete"), EditLineConstString("Invoke completion"), complete_callback); // General bindings we don't mind being overridden if (!multiline) { el_set(m_editline, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string } el_set(m_editline, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash in emacs mode el_set(m_editline, EL_BIND, "\t", "lldb-complete", NULL); // Bind TAB to auto complete // Allow user-specific customization prior to registering bindings we // absolutely require el_source(m_editline, NULL); // Register an internal binding that external developers shouldn't use el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-revert-line"), EditLineConstString("Revert line to saved state"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->RevertLineCommand(ch); })); // Register keys that perform auto-indent correction if (m_fix_indentation_callback && m_fix_indentation_callback_chars) { char bind_key[2] = {0, 0}; const char *indent_chars = m_fix_indentation_callback_chars; while (*indent_chars) { bind_key[0] = *indent_chars; el_set(m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL); ++indent_chars; } } // Multi-line editor bindings if (multiline) { el_set(m_editline, EL_BIND, "\n", "lldb-end-or-add-line", NULL); el_set(m_editline, EL_BIND, "\r", "lldb-end-or-add-line", NULL); el_set(m_editline, EL_BIND, ESCAPE "\n", "lldb-break-line", NULL); el_set(m_editline, EL_BIND, ESCAPE "\r", "lldb-break-line", NULL); el_set(m_editline, EL_BIND, "^p", "lldb-previous-line", NULL); el_set(m_editline, EL_BIND, "^n", "lldb-next-line", NULL); el_set(m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL); el_set(m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL); el_set(m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL); el_set(m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL); // Editor-specific bindings if (IsEmacs()) { el_set(m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL); el_set(m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL); el_set(m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL); el_set(m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL); el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[A", "lldb-previous-history", NULL); el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[B", "lldb-next-history", NULL); el_set(m_editline, EL_BIND, ESCAPE "[1;3A", "lldb-previous-history", NULL); el_set(m_editline, EL_BIND, ESCAPE "[1;3B", "lldb-next-history", NULL); } else { el_set(m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL); el_set(m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line", NULL); el_set(m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL); el_set(m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL); el_set(m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char", NULL); el_set(m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char", NULL); // Escape is absorbed exiting edit mode, so re-register important // sequences // without the prefix el_set(m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL); el_set(m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL); el_set(m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL); } } } //------------------------------------------------------------------ // Editline public methods //------------------------------------------------------------------ Editline *Editline::InstanceFor(EditLine *editline) { Editline *editor; el_get(editline, EL_CLIENTDATA, &editor); return editor; } Editline::Editline(const char *editline_name, FILE *input_file, FILE *output_file, FILE *error_file, bool color_prompts) : m_editor_status(EditorStatus::Complete), m_color_prompts(color_prompts), m_input_file(input_file), m_output_file(output_file), m_error_file(error_file), m_input_connection(fileno(input_file), false) { // Get a shared history instance m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name; m_history_sp = EditlineHistory::GetHistory(m_editor_name); #ifdef USE_SETUPTERM_WORKAROUND if (m_output_file) { const int term_fd = fileno(m_output_file); if (term_fd != -1) { static std::mutex *g_init_terminal_fds_mutex_ptr = nullptr; static std::set *g_init_terminal_fds_ptr = nullptr; static llvm::once_flag g_once_flag; llvm::call_once(g_once_flag, [&]() { g_init_terminal_fds_mutex_ptr = new std::mutex(); // NOTE: Leak to avoid C++ destructor chain issues g_init_terminal_fds_ptr = new std::set(); // NOTE: Leak to avoid // C++ destructor chain // issues }); // We must make sure to initialize the terminal a given file descriptor // only once. If we do this multiple times, we start leaking memory. std::lock_guard guard(*g_init_terminal_fds_mutex_ptr); if (g_init_terminal_fds_ptr->find(term_fd) == g_init_terminal_fds_ptr->end()) { g_init_terminal_fds_ptr->insert(term_fd); setupterm((char *)0, term_fd, (int *)0); } } } #endif } Editline::~Editline() { if (m_editline) { // Disable edit mode to stop the terminal from flushing all input // during the call to el_end() since we expect to have multiple editline // instances in this program. el_set(m_editline, EL_EDITMODE, 0); el_end(m_editline); m_editline = nullptr; } // EditlineHistory objects are sometimes shared between multiple // Editline instances with the same program name. So just release // our shared pointer and if we are the last owner, it will save the // history to the history save file automatically. m_history_sp.reset(); } void Editline::SetPrompt(const char *prompt) { m_set_prompt = prompt == nullptr ? "" : prompt; } void Editline::SetContinuationPrompt(const char *continuation_prompt) { m_set_continuation_prompt = continuation_prompt == nullptr ? "" : continuation_prompt; } void Editline::TerminalSizeChanged() { if (m_editline != nullptr) { el_resize(m_editline); int columns; // Despite the man page claiming non-zero indicates success, it's actually // zero if (el_get(m_editline, EL_GETTC, "co", &columns) == 0) { m_terminal_width = columns; if (m_current_line_rows != -1) { const LineInfoW *info = el_wline(m_editline); int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth()); m_current_line_rows = (lineLength / columns) + 1; } } else { m_terminal_width = INT_MAX; m_current_line_rows = 1; } } } const char *Editline::GetPrompt() { return m_set_prompt.c_str(); } uint32_t Editline::GetCurrentLine() { return m_current_line_index; } bool Editline::Interrupt() { bool result = true; std::lock_guard guard(m_output_mutex); if (m_editor_status == EditorStatus::Editing) { fprintf(m_output_file, "^C\n"); result = m_input_connection.InterruptRead(); } m_editor_status = EditorStatus::Interrupted; return result; } bool Editline::Cancel() { bool result = true; std::lock_guard guard(m_output_mutex); if (m_editor_status == EditorStatus::Editing) { MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); fprintf(m_output_file, ANSI_CLEAR_BELOW); result = m_input_connection.InterruptRead(); } m_editor_status = EditorStatus::Interrupted; return result; } void Editline::SetAutoCompleteCallback(CompleteCallbackType callback, void *baton) { m_completion_callback = callback; m_completion_callback_baton = baton; } void Editline::SetIsInputCompleteCallback(IsInputCompleteCallbackType callback, void *baton) { m_is_input_complete_callback = callback; m_is_input_complete_callback_baton = baton; } bool Editline::SetFixIndentationCallback(FixIndentationCallbackType callback, void *baton, const char *indent_chars) { m_fix_indentation_callback = callback; m_fix_indentation_callback_baton = baton; m_fix_indentation_callback_chars = indent_chars; return false; } bool Editline::GetLine(std::string &line, bool &interrupted) { ConfigureEditor(false); m_input_lines = std::vector(); m_input_lines.insert(m_input_lines.begin(), EditLineConstString("")); std::lock_guard guard(m_output_mutex); lldbassert(m_editor_status != EditorStatus::Editing); if (m_editor_status == EditorStatus::Interrupted) { m_editor_status = EditorStatus::Complete; interrupted = true; return true; } SetCurrentLine(0); m_in_history = false; m_editor_status = EditorStatus::Editing; m_revert_cursor_index = -1; int count; auto input = el_wgets(m_editline, &count); interrupted = m_editor_status == EditorStatus::Interrupted; if (!interrupted) { if (input == nullptr) { fprintf(m_output_file, "\n"); m_editor_status = EditorStatus::EndOfInput; } else { m_history_sp->Enter(input); #if LLDB_EDITLINE_USE_WCHAR line = m_utf8conv.to_bytes(SplitLines(input)[0]); #else line = SplitLines(input)[0]; #endif m_editor_status = EditorStatus::Complete; } } return m_editor_status != EditorStatus::EndOfInput; } bool Editline::GetLines(int first_line_number, StringList &lines, bool &interrupted) { ConfigureEditor(true); // Print the initial input lines, then move the cursor back up to the start of // input SetBaseLineNumber(first_line_number); m_input_lines = std::vector(); m_input_lines.insert(m_input_lines.begin(), EditLineConstString("")); std::lock_guard guard(m_output_mutex); // Begin the line editing loop DisplayInput(); SetCurrentLine(0); MoveCursor(CursorLocation::BlockEnd, CursorLocation::BlockStart); m_editor_status = EditorStatus::Editing; m_in_history = false; m_revert_cursor_index = -1; while (m_editor_status == EditorStatus::Editing) { int count; m_current_line_rows = -1; el_wpush(m_editline, EditLineConstString( "\x1b[^")); // Revert to the existing line content el_wgets(m_editline, &count); } interrupted = m_editor_status == EditorStatus::Interrupted; if (!interrupted) { // Save the completed entry in history before returning m_history_sp->Enter(CombineLines(m_input_lines).c_str()); lines = GetInputAsStringList(); } return m_editor_status != EditorStatus::EndOfInput; } void Editline::PrintAsync(Stream *stream, const char *s, size_t len) { std::lock_guard guard(m_output_mutex); if (m_editor_status == EditorStatus::Editing) { MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); fprintf(m_output_file, ANSI_CLEAR_BELOW); } stream->Write(s, len); stream->Flush(); if (m_editor_status == EditorStatus::Editing) { DisplayInput(); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); } } -bool Editline::CompleteCharacter(char ch, EditLineCharType &out) { +bool Editline::CompleteCharacter(char ch, EditLineGetCharType &out) { #if !LLDB_EDITLINE_USE_WCHAR if (ch == (char)EOF) return false; - out = ch; + out = (unsigned char)ch; return true; #else std::codecvt_utf8 cvt; llvm::SmallString<4> input; for (;;) { const char *from_next; wchar_t *to_next; std::mbstate_t state = std::mbstate_t(); input.push_back(ch); switch (cvt.in(state, input.begin(), input.end(), from_next, &out, &out + 1, to_next)) { case std::codecvt_base::ok: return out != WEOF; case std::codecvt_base::error: case std::codecvt_base::noconv: return false; case std::codecvt_base::partial: lldb::ConnectionStatus status; size_t read_count = m_input_connection.Read( &ch, 1, std::chrono::seconds(0), status, nullptr); if (read_count == 0) return false; break; } } #endif } Index: vendor/lldb/dist/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp (revision 319149) +++ vendor/lldb/dist/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp (revision 319150) @@ -1,296 +1,297 @@ //===-- RenderScriptx86ABIFixups.cpp ----------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes #include // Other libraries and framework includes #include "llvm/ADT/StringRef.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/IRReader/IRReader.h" #include "llvm/Pass.h" // Project includes #include "lldb/Target/Process.h" #include "lldb/Utility/Log.h" using namespace lldb_private; namespace { bool isRSAPICall(llvm::Module &module, llvm::CallInst *call_inst) { // TODO get the list of renderscript modules from lldb and check if // this llvm::Module calls into any of them. (void)module; const auto func_name = call_inst->getCalledFunction()->getName(); if (func_name.startswith("llvm") || func_name.startswith("lldb")) return false; if (call_inst->getCalledFunction()->isIntrinsic()) return false; return true; } bool isRSLargeReturnCall(llvm::Module &module, llvm::CallInst *call_inst) { // i686 and x86_64 returns for large vectors in the RenderScript API are not // handled as normal // register pairs, but as a hidden sret type. This is not reflected in the // debug info or mangled // symbol name, and the android ABI for x86 and x86_64, (as well as the // emulators) specifies there is // no AVX, so bcc generates an sret function because we cannot natively return // 256 bit vectors. // This function simply checks whether a function has a > 128bit return type. // It is perhaps an // unreliable heuristic, and relies on bcc not generating AVX code, so if the // android ABI one day // provides for AVX, this function may go out of fashion. (void)module; if (!call_inst || !call_inst->getCalledFunction()) return false; return call_inst->getCalledFunction() ->getReturnType() ->getPrimitiveSizeInBits() > 128; } bool isRSAllocationPtrTy(const llvm::Type *type) { if (!type->isPointerTy()) return false; auto ptr_type = type->getPointerElementType(); return ptr_type->isStructTy() && ptr_type->getStructName().startswith("struct.rs_allocation"); } bool isRSAllocationTyCallSite(llvm::Module &module, llvm::CallInst *call_inst) { (void)module; if (!call_inst->hasByValArgument()) return false; for (const auto ¶m : call_inst->operand_values()) if (isRSAllocationPtrTy(param->getType())) return true; return false; } llvm::FunctionType *cloneToStructRetFnTy(llvm::CallInst *call_inst) { // on x86 StructReturn functions return a pointer to the return value, rather // than the return // value itself [ref](http://www.agner.org/optimize/calling_conventions.pdf // section 6). // We create a return type by getting the pointer type of the old return type, // and inserting a new // initial argument of pointer type of the original return type. Log *log( GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_EXPRESSIONS)); assert(call_inst && "no CallInst"); llvm::Function *orig = call_inst->getCalledFunction(); assert(orig && "CallInst has no called function"); llvm::FunctionType *orig_type = orig->getFunctionType(); auto name = orig->getName(); if (log) log->Printf("%s - cloning to StructRet function for '%s'", __FUNCTION__, name.str().c_str()); unsigned num_params = orig_type->getNumParams(); std::vector new_params{num_params + 1, nullptr}; std::vector params{orig_type->param_begin(), orig_type->param_end()}; // This may not work if the function is somehow declared void as llvm is // strongly typed // and represents void* with i8* assert(!orig_type->getReturnType()->isVoidTy() && "Cannot add StructRet attribute to void function"); llvm::PointerType *return_type_ptr_type = llvm::PointerType::getUnqual(orig->getReturnType()); assert(return_type_ptr_type && "failed to get function return type PointerType"); if (!return_type_ptr_type) return nullptr; if (log) log->Printf("%s - return type pointer type for StructRet clone @ '0x%p':\n", __FUNCTION__, (void *)return_type_ptr_type); // put the the sret pointer argument in place at the beginning of the argument // list. params.emplace(params.begin(), return_type_ptr_type); assert(params.size() == num_params + 1); return llvm::FunctionType::get(return_type_ptr_type, params, orig->isVarArg()); } bool findRSCallSites(llvm::Module &module, std::set &rs_callsites, bool (*predicate)(llvm::Module &, llvm::CallInst *)) { bool found = false; for (auto &func : module.getFunctionList()) for (auto &block : func.getBasicBlockList()) for (auto &inst : block) { llvm::CallInst *call_inst = llvm::dyn_cast_or_null(&inst); if (!call_inst || !call_inst->getCalledFunction()) // This is not the call-site you are looking for... continue; if (isRSAPICall(module, call_inst) && predicate(module, call_inst)) { rs_callsites.insert(call_inst); found = true; } } return found; } bool fixupX86StructRetCalls(llvm::Module &module) { bool changed = false; // changing a basic block while iterating over it seems to have some undefined // behaviour // going on so we find all RS callsites first, then fix them up after // consuming // the iterator. std::set rs_callsites; if (!findRSCallSites(module, rs_callsites, isRSLargeReturnCall)) return false; for (auto call_inst : rs_callsites) { llvm::FunctionType *new_func_type = cloneToStructRetFnTy(call_inst); assert(new_func_type && "failed to clone functionType for Renderscript ABI fixup"); llvm::CallSite call_site(call_inst); llvm::Function *func = call_inst->getCalledFunction(); assert(func && "cannot resolve function in RenderScriptRuntime"); // Copy the original call arguments std::vector new_call_args(call_site.arg_begin(), call_site.arg_end()); // Allocate enough space to store the return value of the original function // we pass a pointer to this allocation as the StructRet param, and then // copy its // value into the lldb return value const llvm::DataLayout &DL = module.getDataLayout(); llvm::AllocaInst *return_value_alloc = new llvm::AllocaInst( func->getReturnType(), DL.getAllocaAddrSpace(), "var_vector_return_alloc", call_inst); // use the new allocation as the new first argument new_call_args.emplace(new_call_args.begin(), llvm::cast(return_value_alloc)); llvm::PointerType *new_func_ptr_type = llvm::PointerType::get(new_func_type, 0); // Create the type cast from the old function type to the new one llvm::Constant *new_func_cast = llvm::ConstantExpr::getCast( llvm::Instruction::BitCast, func, new_func_ptr_type); // create an allocation for a new function pointer llvm::AllocaInst *new_func_ptr = new llvm::AllocaInst(new_func_ptr_type, DL.getAllocaAddrSpace(), "new_func_ptr", call_inst); // store the new_func_cast to the newly allocated space (new llvm::StoreInst(new_func_cast, new_func_ptr, call_inst)) ->setName("new_func_ptr_load_cast"); // load the new function address ready for a jump llvm::LoadInst *new_func_addr_load = new llvm::LoadInst(new_func_ptr, "load_func_pointer", call_inst); // and create a callinstruction from it llvm::CallInst *new_call_inst = llvm::CallInst::Create( new_func_addr_load, new_call_args, "new_func_call", call_inst); new_call_inst->setCallingConv(call_inst->getCallingConv()); new_call_inst->setTailCall(call_inst->isTailCall()); llvm::LoadInst *lldb_save_result_address = new llvm::LoadInst(return_value_alloc, "save_return_val", call_inst); // Now remove the old broken call call_inst->replaceAllUsesWith(lldb_save_result_address); call_inst->eraseFromParent(); changed = true; } return changed; } bool fixupRSAllocationStructByValCalls(llvm::Module &module) { // On x86_64, calls to functions in the RS runtime that take an // `rs_allocation` type argument // are actually handled as by-ref params by bcc, but appear to be passed by // value by lldb (the callsite all use // `struct byval`). // On x86_64 Linux, struct arguments are transferred in registers if the // struct size is no bigger than // 128bits [ref](http://www.agner.org/optimize/calling_conventions.pdf) // section 7.1 "Passing and returning objects" // otherwise passed on the stack. // an object of type `rs_allocation` is actually 256bits, so should be passed // on the stack. However, code generated // by bcc actually treats formal params of type `rs_allocation` as // `rs_allocation *` so we need to convert the // calling convention to pass by reference, and remove any hint of byval from // formal parameters. bool changed = false; std::set rs_callsites; if (!findRSCallSites(module, rs_callsites, isRSAllocationTyCallSite)) return false; std::set rs_functions; // for all call instructions for (auto call_inst : rs_callsites) { // add the called function to a set so that we can strip its byval // attributes in another pass rs_functions.insert(call_inst->getCalledFunction()); // get the function attributes llvm::AttributeList call_attribs = call_inst->getAttributes(); // iterate over the argument attributes - for (size_t i = 1; i <= call_attribs.getNumSlots(); ++i) { + for (unsigned I = call_attribs.index_begin(); I != call_attribs.index_end(); + I++) { // if this argument is passed by val - if (call_attribs.hasAttribute(i, llvm::Attribute::ByVal)) { + if (call_attribs.hasAttribute(I, llvm::Attribute::ByVal)) { // strip away the byval attribute - call_inst->removeAttribute(i, llvm::Attribute::ByVal); + call_inst->removeAttribute(I, llvm::Attribute::ByVal); changed = true; } } } // for all called function decls for (auto func : rs_functions) { // inspect all of the arguments in the call for (auto &arg : func->args()) { if (arg.hasByValAttr()) { arg.removeAttr(llvm::Attribute::ByVal); changed = true; } } } return changed; } } // end anonymous namespace namespace lldb_private { namespace lldb_renderscript { bool fixupX86FunctionCalls(llvm::Module &module) { return fixupX86StructRetCalls(module); } bool fixupX86_64FunctionCalls(llvm::Module &module) { bool changed = false; changed |= fixupX86StructRetCalls(module); changed |= fixupRSAllocationStructByValCalls(module); return changed; } } // end namespace lldb_renderscript } // end namespace lldb_private Index: vendor/lldb/dist/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp (revision 319149) +++ vendor/lldb/dist/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp (revision 319150) @@ -1,1459 +1,1463 @@ //===-- ProcessMonitor.cpp ------------------------------------ -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes #include #include #include #include #include #include #include #include #include #include // C++ Includes // Other libraries and framework includes #include "lldb/Core/RegisterValue.h" #include "lldb/Core/Scalar.h" #include "lldb/Host/Host.h" #include "lldb/Host/PseudoTerminal.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Thread.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/Status.h" #include "FreeBSDThread.h" #include "Plugins/Process/POSIX/CrashReason.h" #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "ProcessFreeBSD.h" #include "ProcessMonitor.h" extern "C" { extern char **environ; } using namespace lldb; using namespace lldb_private; // We disable the tracing of ptrace calls for integration builds to // avoid the additional indirection and checks. #ifndef LLDB_CONFIGURATION_BUILDANDINTEGRATION // Wrapper for ptrace to catch errors and log calls. const char *Get_PT_IO_OP(int op) { switch (op) { case PIOD_READ_D: return "READ_D"; case PIOD_WRITE_D: return "WRITE_D"; case PIOD_READ_I: return "READ_I"; case PIOD_WRITE_I: return "WRITE_I"; default: return "Unknown op"; } } // Wrapper for ptrace to catch errors and log calls. // Note that ptrace sets errno on error because -1 is reserved as a valid // result. extern long PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data, const char *reqName, const char *file, int line) { long int result; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); if (log) { log->Printf("ptrace(%s, %" PRIu64 ", %p, %x) called from file %s line %d", reqName, pid, addr, data, file, line); if (req == PT_IO) { struct ptrace_io_desc *pi = (struct ptrace_io_desc *)addr; log->Printf("PT_IO: op=%s offs=%zx size=%zu", Get_PT_IO_OP(pi->piod_op), (size_t)pi->piod_offs, pi->piod_len); } } // PtraceDisplayBytes(req, data); errno = 0; result = ptrace(req, pid, (caddr_t)addr, data); // PtraceDisplayBytes(req, data); if (log && errno != 0) { const char *str; switch (errno) { case ESRCH: str = "ESRCH"; break; case EINVAL: str = "EINVAL"; break; case EBUSY: str = "EBUSY"; break; case EPERM: str = "EPERM"; break; default: str = ""; } log->Printf("ptrace() failed; errno=%d (%s)", errno, str); } if (log) { #ifdef __amd64__ if (req == PT_GETREGS) { struct reg *r = (struct reg *)addr; log->Printf("PT_GETREGS: rip=0x%lx rsp=0x%lx rbp=0x%lx rax=0x%lx", r->r_rip, r->r_rsp, r->r_rbp, r->r_rax); } if (req == PT_GETDBREGS || req == PT_SETDBREGS) { struct dbreg *r = (struct dbreg *)addr; char setget = (req == PT_GETDBREGS) ? 'G' : 'S'; for (int i = 0; i <= 7; i++) log->Printf("PT_%cETDBREGS: dr[%d]=0x%lx", setget, i, r->dr[i]); } #endif } return result; } // Wrapper for ptrace when logging is not required. // Sets errno to 0 prior to calling ptrace. extern long PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data) { long result = 0; errno = 0; result = ptrace(req, pid, (caddr_t)addr, data); return result; } #define PTRACE(req, pid, addr, data) \ PtraceWrapper((req), (pid), (addr), (data), #req, __FILE__, __LINE__) #else PtraceWrapper((req), (pid), (addr), (data)) #endif //------------------------------------------------------------------------------ // Static implementations of ProcessMonitor::ReadMemory and // ProcessMonitor::WriteMemory. This enables mutual recursion between these // functions without needed to go thru the thread funnel. static size_t DoReadMemory(lldb::pid_t pid, lldb::addr_t vm_addr, void *buf, size_t size, Status &error) { struct ptrace_io_desc pi_desc; pi_desc.piod_op = PIOD_READ_D; pi_desc.piod_offs = (void *)vm_addr; pi_desc.piod_addr = buf; pi_desc.piod_len = size; if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0) error.SetErrorToErrno(); return pi_desc.piod_len; } static size_t DoWriteMemory(lldb::pid_t pid, lldb::addr_t vm_addr, const void *buf, size_t size, Status &error) { struct ptrace_io_desc pi_desc; pi_desc.piod_op = PIOD_WRITE_D; pi_desc.piod_offs = (void *)vm_addr; pi_desc.piod_addr = (void *)buf; pi_desc.piod_len = size; if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0) error.SetErrorToErrno(); return pi_desc.piod_len; } // Simple helper function to ensure flags are enabled on the given file // descriptor. static bool EnsureFDFlags(int fd, int flags, Status &error) { int status; if ((status = fcntl(fd, F_GETFL)) == -1) { error.SetErrorToErrno(); return false; } if (fcntl(fd, F_SETFL, status | flags) == -1) { error.SetErrorToErrno(); return false; } return true; } //------------------------------------------------------------------------------ /// @class Operation /// @brief Represents a ProcessMonitor operation. /// /// Under FreeBSD, it is not possible to ptrace() from any other thread but the /// one that spawned or attached to the process from the start. Therefore, when /// a ProcessMonitor is asked to deliver or change the state of an inferior /// process the operation must be "funneled" to a specific thread to perform the /// task. The Operation class provides an abstract base for all services the /// ProcessMonitor must perform via the single virtual function Execute, thus /// encapsulating the code that needs to run in the privileged context. class Operation { public: virtual ~Operation() {} virtual void Execute(ProcessMonitor *monitor) = 0; }; //------------------------------------------------------------------------------ /// @class ReadOperation /// @brief Implements ProcessMonitor::ReadMemory. class ReadOperation : public Operation { public: ReadOperation(lldb::addr_t addr, void *buff, size_t size, Status &error, size_t &result) : m_addr(addr), m_buff(buff), m_size(size), m_error(error), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::addr_t m_addr; void *m_buff; size_t m_size; Status &m_error; size_t &m_result; }; void ReadOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); m_result = DoReadMemory(pid, m_addr, m_buff, m_size, m_error); } //------------------------------------------------------------------------------ /// @class WriteOperation /// @brief Implements ProcessMonitor::WriteMemory. class WriteOperation : public Operation { public: WriteOperation(lldb::addr_t addr, const void *buff, size_t size, Status &error, size_t &result) : m_addr(addr), m_buff(buff), m_size(size), m_error(error), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::addr_t m_addr; const void *m_buff; size_t m_size; Status &m_error; size_t &m_result; }; void WriteOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); m_result = DoWriteMemory(pid, m_addr, m_buff, m_size, m_error); } //------------------------------------------------------------------------------ /// @class ReadRegOperation /// @brief Implements ProcessMonitor::ReadRegisterValue. class ReadRegOperation : public Operation { public: ReadRegOperation(lldb::tid_t tid, unsigned offset, unsigned size, RegisterValue &value, bool &result) : m_tid(tid), m_offset(offset), m_size(size), m_value(value), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned m_offset; unsigned m_size; RegisterValue &m_value; bool &m_result; }; void ReadRegOperation::Execute(ProcessMonitor *monitor) { struct reg regs; int rc; if ((rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)®s, 0)) < 0) { m_result = false; } else { // 'struct reg' contains only 32- or 64-bit register values. Punt on // others. Also, not all entries may be uintptr_t sized, such as 32-bit // processes on powerpc64 (probably the same for i386 on amd64) if (m_size == sizeof(uint32_t)) m_value = *(uint32_t *)(((caddr_t)®s) + m_offset); else if (m_size == sizeof(uint64_t)) m_value = *(uint64_t *)(((caddr_t)®s) + m_offset); else memcpy((void *)&m_value, (((caddr_t)®s) + m_offset), m_size); m_result = true; } } //------------------------------------------------------------------------------ /// @class WriteRegOperation /// @brief Implements ProcessMonitor::WriteRegisterValue. class WriteRegOperation : public Operation { public: WriteRegOperation(lldb::tid_t tid, unsigned offset, const RegisterValue &value, bool &result) : m_tid(tid), m_offset(offset), m_value(value), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned m_offset; const RegisterValue &m_value; bool &m_result; }; void WriteRegOperation::Execute(ProcessMonitor *monitor) { struct reg regs; if (PTRACE(PT_GETREGS, m_tid, (caddr_t)®s, 0) < 0) { m_result = false; return; } *(uintptr_t *)(((caddr_t)®s) + m_offset) = (uintptr_t)m_value.GetAsUInt64(); if (PTRACE(PT_SETREGS, m_tid, (caddr_t)®s, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class ReadDebugRegOperation /// @brief Implements ProcessMonitor::ReadDebugRegisterValue. class ReadDebugRegOperation : public Operation { public: ReadDebugRegOperation(lldb::tid_t tid, unsigned offset, unsigned size, RegisterValue &value, bool &result) : m_tid(tid), m_offset(offset), m_size(size), m_value(value), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned m_offset; unsigned m_size; RegisterValue &m_value; bool &m_result; }; void ReadDebugRegOperation::Execute(ProcessMonitor *monitor) { struct dbreg regs; int rc; if ((rc = PTRACE(PT_GETDBREGS, m_tid, (caddr_t)®s, 0)) < 0) { m_result = false; } else { if (m_size == sizeof(uintptr_t)) m_value = *(uintptr_t *)(((caddr_t)®s) + m_offset); else memcpy((void *)&m_value, (((caddr_t)®s) + m_offset), m_size); m_result = true; } } //------------------------------------------------------------------------------ /// @class WriteDebugRegOperation /// @brief Implements ProcessMonitor::WriteDebugRegisterValue. class WriteDebugRegOperation : public Operation { public: WriteDebugRegOperation(lldb::tid_t tid, unsigned offset, const RegisterValue &value, bool &result) : m_tid(tid), m_offset(offset), m_value(value), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned m_offset; const RegisterValue &m_value; bool &m_result; }; void WriteDebugRegOperation::Execute(ProcessMonitor *monitor) { struct dbreg regs; if (PTRACE(PT_GETDBREGS, m_tid, (caddr_t)®s, 0) < 0) { m_result = false; return; } *(uintptr_t *)(((caddr_t)®s) + m_offset) = (uintptr_t)m_value.GetAsUInt64(); if (PTRACE(PT_SETDBREGS, m_tid, (caddr_t)®s, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class ReadGPROperation /// @brief Implements ProcessMonitor::ReadGPR. class ReadGPROperation : public Operation { public: ReadGPROperation(lldb::tid_t tid, void *buf, bool &result) : m_tid(tid), m_buf(buf), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_buf; bool &m_result; }; void ReadGPROperation::Execute(ProcessMonitor *monitor) { int rc; errno = 0; rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)m_buf, 0); if (errno != 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class ReadFPROperation /// @brief Implements ProcessMonitor::ReadFPR. class ReadFPROperation : public Operation { public: ReadFPROperation(lldb::tid_t tid, void *buf, bool &result) : m_tid(tid), m_buf(buf), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_buf; bool &m_result; }; void ReadFPROperation::Execute(ProcessMonitor *monitor) { if (PTRACE(PT_GETFPREGS, m_tid, (caddr_t)m_buf, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class WriteGPROperation /// @brief Implements ProcessMonitor::WriteGPR. class WriteGPROperation : public Operation { public: WriteGPROperation(lldb::tid_t tid, void *buf, bool &result) : m_tid(tid), m_buf(buf), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_buf; bool &m_result; }; void WriteGPROperation::Execute(ProcessMonitor *monitor) { if (PTRACE(PT_SETREGS, m_tid, (caddr_t)m_buf, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class WriteFPROperation /// @brief Implements ProcessMonitor::WriteFPR. class WriteFPROperation : public Operation { public: WriteFPROperation(lldb::tid_t tid, void *buf, bool &result) : m_tid(tid), m_buf(buf), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_buf; bool &m_result; }; void WriteFPROperation::Execute(ProcessMonitor *monitor) { if (PTRACE(PT_SETFPREGS, m_tid, (caddr_t)m_buf, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class ResumeOperation /// @brief Implements ProcessMonitor::Resume. class ResumeOperation : public Operation { public: ResumeOperation(uint32_t signo, bool &result) : m_signo(signo), m_result(result) {} void Execute(ProcessMonitor *monitor); private: uint32_t m_signo; bool &m_result; }; void ResumeOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); int data = 0; if (m_signo != LLDB_INVALID_SIGNAL_NUMBER) data = m_signo; if (PTRACE(PT_CONTINUE, pid, (caddr_t)1, data)) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); if (log) log->Printf("ResumeOperation (%" PRIu64 ") failed: %s", pid, strerror(errno)); m_result = false; } else m_result = true; } //------------------------------------------------------------------------------ /// @class SingleStepOperation /// @brief Implements ProcessMonitor::SingleStep. class SingleStepOperation : public Operation { public: SingleStepOperation(uint32_t signo, bool &result) : m_signo(signo), m_result(result) {} void Execute(ProcessMonitor *monitor); private: uint32_t m_signo; bool &m_result; }; void SingleStepOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); int data = 0; if (m_signo != LLDB_INVALID_SIGNAL_NUMBER) data = m_signo; if (PTRACE(PT_STEP, pid, NULL, data)) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class LwpInfoOperation /// @brief Implements ProcessMonitor::GetLwpInfo. class LwpInfoOperation : public Operation { public: LwpInfoOperation(lldb::tid_t tid, void *info, bool &result, int &ptrace_err) : m_tid(tid), m_info(info), m_result(result), m_err(ptrace_err) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_info; bool &m_result; int &m_err; }; void LwpInfoOperation::Execute(ProcessMonitor *monitor) { struct ptrace_lwpinfo plwp; if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp))) { m_result = false; m_err = errno; } else { memcpy(m_info, &plwp, sizeof(plwp)); m_result = true; } } //------------------------------------------------------------------------------ /// @class ThreadSuspendOperation /// @brief Implements ProcessMonitor::ThreadSuspend. class ThreadSuspendOperation : public Operation { public: ThreadSuspendOperation(lldb::tid_t tid, bool suspend, bool &result) : m_tid(tid), m_suspend(suspend), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; bool m_suspend; bool &m_result; }; void ThreadSuspendOperation::Execute(ProcessMonitor *monitor) { m_result = !PTRACE(m_suspend ? PT_SUSPEND : PT_RESUME, m_tid, NULL, 0); } //------------------------------------------------------------------------------ /// @class EventMessageOperation /// @brief Implements ProcessMonitor::GetEventMessage. class EventMessageOperation : public Operation { public: EventMessageOperation(lldb::tid_t tid, unsigned long *message, bool &result) : m_tid(tid), m_message(message), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned long *m_message; bool &m_result; }; void EventMessageOperation::Execute(ProcessMonitor *monitor) { struct ptrace_lwpinfo plwp; if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp))) m_result = false; else { if (plwp.pl_flags & PL_FLAG_FORKED) { *m_message = plwp.pl_child_pid; m_result = true; } else m_result = false; } } //------------------------------------------------------------------------------ /// @class KillOperation /// @brief Implements ProcessMonitor::Kill. class KillOperation : public Operation { public: KillOperation(bool &result) : m_result(result) {} void Execute(ProcessMonitor *monitor); private: bool &m_result; }; void KillOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); if (PTRACE(PT_KILL, pid, NULL, 0)) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class DetachOperation /// @brief Implements ProcessMonitor::Detach. class DetachOperation : public Operation { public: DetachOperation(Status &result) : m_error(result) {} void Execute(ProcessMonitor *monitor); private: Status &m_error; }; void DetachOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); if (PTRACE(PT_DETACH, pid, NULL, 0) < 0) m_error.SetErrorToErrno(); } ProcessMonitor::OperationArgs::OperationArgs(ProcessMonitor *monitor) : m_monitor(monitor) { sem_init(&m_semaphore, 0, 0); } ProcessMonitor::OperationArgs::~OperationArgs() { sem_destroy(&m_semaphore); } ProcessMonitor::LaunchArgs::LaunchArgs(ProcessMonitor *monitor, lldb_private::Module *module, char const **argv, char const **envp, const FileSpec &stdin_file_spec, const FileSpec &stdout_file_spec, const FileSpec &stderr_file_spec, const FileSpec &working_dir) : OperationArgs(monitor), m_module(module), m_argv(argv), m_envp(envp), m_stdin_file_spec(stdin_file_spec), m_stdout_file_spec(stdout_file_spec), m_stderr_file_spec(stderr_file_spec), m_working_dir(working_dir) {} ProcessMonitor::LaunchArgs::~LaunchArgs() {} ProcessMonitor::AttachArgs::AttachArgs(ProcessMonitor *monitor, lldb::pid_t pid) : OperationArgs(monitor), m_pid(pid) {} ProcessMonitor::AttachArgs::~AttachArgs() {} //------------------------------------------------------------------------------ /// The basic design of the ProcessMonitor is built around two threads. /// /// One thread (@see SignalThread) simply blocks on a call to waitpid() looking /// for changes in the debugee state. When a change is detected a /// ProcessMessage is sent to the associated ProcessFreeBSD instance. This /// thread /// "drives" state changes in the debugger. /// /// The second thread (@see OperationThread) is responsible for two things 1) /// launching or attaching to the inferior process, and then 2) servicing /// operations such as register reads/writes, stepping, etc. See the comments /// on the Operation class for more info as to why this is needed. ProcessMonitor::ProcessMonitor( ProcessFreeBSD *process, Module *module, const char *argv[], const char *envp[], const FileSpec &stdin_file_spec, const FileSpec &stdout_file_spec, const FileSpec &stderr_file_spec, const FileSpec &working_dir, const lldb_private::ProcessLaunchInfo & /* launch_info */, lldb_private::Status &error) : m_process(static_cast(process)), m_pid(LLDB_INVALID_PROCESS_ID), m_terminal_fd(-1), m_operation(0) { using namespace std::placeholders; std::unique_ptr args( new LaunchArgs(this, module, argv, envp, stdin_file_spec, stdout_file_spec, stderr_file_spec, working_dir)); sem_init(&m_operation_pending, 0, 0); sem_init(&m_operation_done, 0, 0); StartLaunchOpThread(args.get(), error); if (!error.Success()) return; WAIT_AGAIN: // Wait for the operation thread to initialize. if (sem_wait(&args->m_semaphore)) { if (errno == EINTR) goto WAIT_AGAIN; else { error.SetErrorToErrno(); return; } } // Check that the launch was a success. if (!args->m_error.Success()) { StopOpThread(); error = args->m_error; return; } // Finally, start monitoring the child process for change in state. m_monitor_thread = Host::StartMonitoringChildProcess( std::bind(&ProcessMonitor::MonitorCallback, this, _1, _2, _3, _4), GetPID(), true); if (!m_monitor_thread.IsJoinable()) { error.SetErrorToGenericError(); error.SetErrorString("Process launch failed."); return; } } ProcessMonitor::ProcessMonitor(ProcessFreeBSD *process, lldb::pid_t pid, lldb_private::Status &error) : m_process(static_cast(process)), m_pid(pid), m_terminal_fd(-1), m_operation(0) { using namespace std::placeholders; sem_init(&m_operation_pending, 0, 0); sem_init(&m_operation_done, 0, 0); std::unique_ptr args(new AttachArgs(this, pid)); StartAttachOpThread(args.get(), error); if (!error.Success()) return; WAIT_AGAIN: // Wait for the operation thread to initialize. if (sem_wait(&args->m_semaphore)) { if (errno == EINTR) goto WAIT_AGAIN; else { error.SetErrorToErrno(); return; } } // Check that the attach was a success. if (!args->m_error.Success()) { StopOpThread(); error = args->m_error; return; } // Finally, start monitoring the child process for change in state. m_monitor_thread = Host::StartMonitoringChildProcess( std::bind(&ProcessMonitor::MonitorCallback, this, _1, _2, _3, _4), GetPID(), true); if (!m_monitor_thread.IsJoinable()) { error.SetErrorToGenericError(); error.SetErrorString("Process attach failed."); return; } } ProcessMonitor::~ProcessMonitor() { StopMonitor(); } //------------------------------------------------------------------------------ // Thread setup and tear down. void ProcessMonitor::StartLaunchOpThread(LaunchArgs *args, Status &error) { static const char *g_thread_name = "lldb.process.freebsd.operation"; if (m_operation_thread.IsJoinable()) return; m_operation_thread = ThreadLauncher::LaunchThread(g_thread_name, LaunchOpThread, args, &error); } void *ProcessMonitor::LaunchOpThread(void *arg) { LaunchArgs *args = static_cast(arg); if (!Launch(args)) { sem_post(&args->m_semaphore); return NULL; } ServeOperation(args); return NULL; } bool ProcessMonitor::Launch(LaunchArgs *args) { ProcessMonitor *monitor = args->m_monitor; ProcessFreeBSD &process = monitor->GetProcess(); const char **argv = args->m_argv; const char **envp = args->m_envp; const FileSpec &stdin_file_spec = args->m_stdin_file_spec; const FileSpec &stdout_file_spec = args->m_stdout_file_spec; const FileSpec &stderr_file_spec = args->m_stderr_file_spec; const FileSpec &working_dir = args->m_working_dir; lldb_utility::PseudoTerminal terminal; const size_t err_len = 1024; char err_str[err_len]; ::pid_t pid; // Propagate the environment if one is not supplied. if (envp == NULL || envp[0] == NULL) envp = const_cast(environ); if ((pid = terminal.Fork(err_str, err_len)) == -1) { args->m_error.SetErrorToGenericError(); args->m_error.SetErrorString("Process fork failed."); goto FINISH; } // Recognized child exit status codes. enum { ePtraceFailed = 1, eDupStdinFailed, eDupStdoutFailed, eDupStderrFailed, eChdirFailed, eExecFailed, eSetGidFailed }; // Child process. if (pid == 0) { // Trace this process. if (PTRACE(PT_TRACE_ME, 0, NULL, 0) < 0) exit(ePtraceFailed); // terminal has already dupped the tty descriptors to stdin/out/err. // This closes original fd from which they were copied (and avoids // leaking descriptors to the debugged process. terminal.CloseSlaveFileDescriptor(); // Do not inherit setgid powers. if (setgid(getgid()) != 0) exit(eSetGidFailed); // Let us have our own process group. setpgid(0, 0); // Dup file descriptors if needed. // // FIXME: If two or more of the paths are the same we needlessly open // the same file multiple times. if (stdin_file_spec) if (!DupDescriptor(stdin_file_spec, STDIN_FILENO, O_RDONLY)) exit(eDupStdinFailed); if (stdout_file_spec) if (!DupDescriptor(stdout_file_spec, STDOUT_FILENO, O_WRONLY | O_CREAT)) exit(eDupStdoutFailed); if (stderr_file_spec) if (!DupDescriptor(stderr_file_spec, STDERR_FILENO, O_WRONLY | O_CREAT)) exit(eDupStderrFailed); // Change working directory if (working_dir && 0 != ::chdir(working_dir.GetCString())) exit(eChdirFailed); // Execute. We should never return. execve(argv[0], const_cast(argv), const_cast(envp)); exit(eExecFailed); } // Wait for the child process to to trap on its call to execve. ::pid_t wpid; int status; if ((wpid = waitpid(pid, &status, 0)) < 0) { args->m_error.SetErrorToErrno(); goto FINISH; } else if (WIFEXITED(status)) { // open, dup or execve likely failed for some reason. args->m_error.SetErrorToGenericError(); switch (WEXITSTATUS(status)) { case ePtraceFailed: args->m_error.SetErrorString("Child ptrace failed."); break; case eDupStdinFailed: args->m_error.SetErrorString("Child open stdin failed."); break; case eDupStdoutFailed: args->m_error.SetErrorString("Child open stdout failed."); break; case eDupStderrFailed: args->m_error.SetErrorString("Child open stderr failed."); break; case eChdirFailed: args->m_error.SetErrorString("Child failed to set working directory."); break; case eExecFailed: args->m_error.SetErrorString("Child exec failed."); break; case eSetGidFailed: args->m_error.SetErrorString("Child setgid failed."); break; default: args->m_error.SetErrorString("Child returned unknown exit status."); break; } goto FINISH; } assert(WIFSTOPPED(status) && wpid == (::pid_t)pid && "Could not sync with inferior process."); #ifdef notyet // Have the child raise an event on exit. This is used to keep the child in // limbo until it is destroyed. if (PTRACE(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACEEXIT) < 0) { args->m_error.SetErrorToErrno(); goto FINISH; } #endif // Release the master terminal descriptor and pass it off to the // ProcessMonitor instance. Similarly stash the inferior pid. monitor->m_terminal_fd = terminal.ReleaseMasterFileDescriptor(); monitor->m_pid = pid; // Set the terminal fd to be in non blocking mode (it simplifies the // implementation of ProcessFreeBSD::GetSTDOUT to have a non-blocking // descriptor to read from). if (!EnsureFDFlags(monitor->m_terminal_fd, O_NONBLOCK, args->m_error)) goto FINISH; process.SendMessage(ProcessMessage::Attach(pid)); FINISH: return args->m_error.Success(); } void ProcessMonitor::StartAttachOpThread(AttachArgs *args, lldb_private::Status &error) { static const char *g_thread_name = "lldb.process.freebsd.operation"; if (m_operation_thread.IsJoinable()) return; m_operation_thread = ThreadLauncher::LaunchThread(g_thread_name, AttachOpThread, args, &error); } void *ProcessMonitor::AttachOpThread(void *arg) { AttachArgs *args = static_cast(arg); Attach(args); ServeOperation(args); return NULL; } void ProcessMonitor::Attach(AttachArgs *args) { lldb::pid_t pid = args->m_pid; ProcessMonitor *monitor = args->m_monitor; ProcessFreeBSD &process = monitor->GetProcess(); if (pid <= 1) { args->m_error.SetErrorToGenericError(); args->m_error.SetErrorString("Attaching to process 1 is not allowed."); return; } // Attach to the requested process. if (PTRACE(PT_ATTACH, pid, NULL, 0) < 0) { args->m_error.SetErrorToErrno(); return; } int status; if ((status = waitpid(pid, NULL, 0)) < 0) { args->m_error.SetErrorToErrno(); return; } process.SendMessage(ProcessMessage::Attach(pid)); } size_t ProcessMonitor::GetCurrentThreadIDs(std::vector &thread_ids) { lwpid_t *tids; int tdcnt; thread_ids.clear(); tdcnt = PTRACE(PT_GETNUMLWPS, m_pid, NULL, 0); if (tdcnt <= 0) return 0; tids = (lwpid_t *)malloc(tdcnt * sizeof(*tids)); if (tids == NULL) return 0; if (PTRACE(PT_GETLWPLIST, m_pid, (void *)tids, tdcnt) < 0) { free(tids); return 0; } thread_ids = std::vector(tids, tids + tdcnt); free(tids); return thread_ids.size(); } bool ProcessMonitor::MonitorCallback(ProcessMonitor *monitor, lldb::pid_t pid, bool exited, int signal, int status) { ProcessMessage message; ProcessFreeBSD *process = monitor->m_process; assert(process); bool stop_monitoring; struct ptrace_lwpinfo plwp; int ptrace_err; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); if (exited) { if (log) log->Printf("ProcessMonitor::%s() got exit signal, tid = %" PRIu64, __FUNCTION__, pid); message = ProcessMessage::Exit(pid, status); process->SendMessage(message); return pid == process->GetID(); } if (!monitor->GetLwpInfo(pid, &plwp, ptrace_err)) stop_monitoring = true; // pid is gone. Bail. else { switch (plwp.pl_siginfo.si_signo) { case SIGTRAP: message = MonitorSIGTRAP(monitor, &plwp.pl_siginfo, plwp.pl_lwpid); break; default: message = MonitorSignal(monitor, &plwp.pl_siginfo, plwp.pl_lwpid); break; } process->SendMessage(message); stop_monitoring = message.GetKind() == ProcessMessage::eExitMessage; } return stop_monitoring; } ProcessMessage ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor, const siginfo_t *info, lldb::tid_t tid) { ProcessMessage message; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); assert(monitor); assert(info && info->si_signo == SIGTRAP && "Unexpected child signal!"); switch (info->si_code) { default: assert(false && "Unexpected SIGTRAP code!"); break; case (SIGTRAP /* | (PTRACE_EVENT_EXIT << 8) */): { // The inferior process is about to exit. Maintain the process in a // state of "limbo" until we are explicitly commanded to detach, // destroy, resume, etc. unsigned long data = 0; if (!monitor->GetEventMessage(tid, &data)) data = -1; if (log) log->Printf("ProcessMonitor::%s() received exit? event, data = %lx, tid " "= %" PRIu64, __FUNCTION__, data, tid); message = ProcessMessage::Limbo(tid, (data >> 8)); break; } case 0: case TRAP_TRACE: +#ifdef TRAP_CAP + // Map TRAP_CAP to a trace trap in the absense of a more specific handler. + case TRAP_CAP: +#endif if (log) log->Printf("ProcessMonitor::%s() received trace event, tid = %" PRIu64 " : si_code = %d", __FUNCTION__, tid, info->si_code); message = ProcessMessage::Trace(tid); break; case SI_KERNEL: case TRAP_BRKPT: if (monitor->m_process->IsSoftwareStepBreakpoint(tid)) { if (log) log->Printf("ProcessMonitor::%s() received sw single step breakpoint " "event, tid = %" PRIu64, __FUNCTION__, tid); message = ProcessMessage::Trace(tid); } else { if (log) log->Printf( "ProcessMonitor::%s() received breakpoint event, tid = %" PRIu64, __FUNCTION__, tid); message = ProcessMessage::Break(tid); } break; } return message; } ProcessMessage ProcessMonitor::MonitorSignal(ProcessMonitor *monitor, const siginfo_t *info, lldb::tid_t tid) { ProcessMessage message; int signo = info->si_signo; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); // POSIX says that process behaviour is undefined after it ignores a SIGFPE, // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a // kill(2) or raise(3). Similarly for tgkill(2) on FreeBSD. // // IOW, user generated signals never generate what we consider to be a // "crash". // // Similarly, ACK signals generated by this monitor. if (info->si_code == SI_USER) { if (log) log->Printf( "ProcessMonitor::%s() received signal %s with code %s, pid = %d", __FUNCTION__, monitor->m_process->GetUnixSignals()->GetSignalAsCString(signo), "SI_USER", info->si_pid); if (info->si_pid == getpid()) return ProcessMessage::SignalDelivered(tid, signo); else return ProcessMessage::Signal(tid, signo); } if (log) log->Printf( "ProcessMonitor::%s() received signal %s", __FUNCTION__, monitor->m_process->GetUnixSignals()->GetSignalAsCString(signo)); switch (signo) { case SIGSEGV: case SIGILL: case SIGFPE: case SIGBUS: lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); const auto reason = GetCrashReason(*info); return ProcessMessage::Crash(tid, reason, signo, fault_addr); } // Everything else is "normal" and does not require any special action on // our part. return ProcessMessage::Signal(tid, signo); } void ProcessMonitor::ServeOperation(OperationArgs *args) { ProcessMonitor *monitor = args->m_monitor; // We are finised with the arguments and are ready to go. Sync with the // parent thread and start serving operations on the inferior. sem_post(&args->m_semaphore); for (;;) { // wait for next pending operation sem_wait(&monitor->m_operation_pending); monitor->m_operation->Execute(monitor); // notify calling thread that operation is complete sem_post(&monitor->m_operation_done); } } void ProcessMonitor::DoOperation(Operation *op) { std::lock_guard guard(m_operation_mutex); m_operation = op; // notify operation thread that an operation is ready to be processed sem_post(&m_operation_pending); // wait for operation to complete sem_wait(&m_operation_done); } size_t ProcessMonitor::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error) { size_t result; ReadOperation op(vm_addr, buf, size, error, result); DoOperation(&op); return result; } size_t ProcessMonitor::WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, lldb_private::Status &error) { size_t result; WriteOperation op(vm_addr, buf, size, error, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name, unsigned size, RegisterValue &value) { bool result; ReadRegOperation op(tid, offset, size, value, result); DoOperation(&op); return result; } bool ProcessMonitor::WriteRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name, const RegisterValue &value) { bool result; WriteRegOperation op(tid, offset, value, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadDebugRegisterValue( lldb::tid_t tid, unsigned offset, const char *reg_name, unsigned size, lldb_private::RegisterValue &value) { bool result; ReadDebugRegOperation op(tid, offset, size, value, result); DoOperation(&op); return result; } bool ProcessMonitor::WriteDebugRegisterValue( lldb::tid_t tid, unsigned offset, const char *reg_name, const lldb_private::RegisterValue &value) { bool result; WriteDebugRegOperation op(tid, offset, value, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadGPR(lldb::tid_t tid, void *buf, size_t buf_size) { bool result; ReadGPROperation op(tid, buf, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size) { bool result; ReadFPROperation op(tid, buf, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset) { return false; } bool ProcessMonitor::WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size) { bool result; WriteGPROperation op(tid, buf, result); DoOperation(&op); return result; } bool ProcessMonitor::WriteFPR(lldb::tid_t tid, void *buf, size_t buf_size) { bool result; WriteFPROperation op(tid, buf, result); DoOperation(&op); return result; } bool ProcessMonitor::WriteRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset) { return false; } bool ProcessMonitor::ReadThreadPointer(lldb::tid_t tid, lldb::addr_t &value) { return false; } bool ProcessMonitor::Resume(lldb::tid_t unused, uint32_t signo) { bool result; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); if (log) { const char *signame = m_process->GetUnixSignals()->GetSignalAsCString(signo); if (signame == nullptr) signame = ""; log->Printf("ProcessMonitor::%s() resuming pid %" PRIu64 " with signal %s", __FUNCTION__, GetPID(), signame); } ResumeOperation op(signo, result); DoOperation(&op); if (log) log->Printf("ProcessMonitor::%s() resuming result = %s", __FUNCTION__, result ? "true" : "false"); return result; } bool ProcessMonitor::SingleStep(lldb::tid_t unused, uint32_t signo) { bool result; SingleStepOperation op(signo, result); DoOperation(&op); return result; } bool ProcessMonitor::Kill() { bool result; KillOperation op(result); DoOperation(&op); return result; } bool ProcessMonitor::GetLwpInfo(lldb::tid_t tid, void *lwpinfo, int &ptrace_err) { bool result; LwpInfoOperation op(tid, lwpinfo, result, ptrace_err); DoOperation(&op); return result; } bool ProcessMonitor::ThreadSuspend(lldb::tid_t tid, bool suspend) { bool result; ThreadSuspendOperation op(tid, suspend, result); DoOperation(&op); return result; } bool ProcessMonitor::GetEventMessage(lldb::tid_t tid, unsigned long *message) { bool result; EventMessageOperation op(tid, message, result); DoOperation(&op); return result; } lldb_private::Status ProcessMonitor::Detach(lldb::tid_t tid) { lldb_private::Status error; if (tid != LLDB_INVALID_THREAD_ID) { DetachOperation op(error); DoOperation(&op); } return error; } bool ProcessMonitor::DupDescriptor(const FileSpec &file_spec, int fd, int flags) { int target_fd = open(file_spec.GetCString(), flags, 0666); if (target_fd == -1) return false; if (dup2(target_fd, fd) == -1) return false; return (close(target_fd) == -1) ? false : true; } void ProcessMonitor::StopMonitoringChildProcess() { if (m_monitor_thread.IsJoinable()) { m_monitor_thread.Cancel(); m_monitor_thread.Join(nullptr); m_monitor_thread.Reset(); } } void ProcessMonitor::StopMonitor() { StopMonitoringChildProcess(); StopOpThread(); sem_destroy(&m_operation_pending); sem_destroy(&m_operation_done); if (m_terminal_fd >= 0) { close(m_terminal_fd); m_terminal_fd = -1; } } // FIXME: On Linux, when a new thread is created, we receive to notifications, // (1) a SIGTRAP|PTRACE_EVENT_CLONE from the main process thread with the // child thread id as additional information, and (2) a SIGSTOP|SI_USER from // the new child thread indicating that it has is stopped because we attached. // We have no guarantee of the order in which these arrive, but we need both // before we are ready to proceed. We currently keep a list of threads which // have sent the initial SIGSTOP|SI_USER event. Then when we receive the // SIGTRAP|PTRACE_EVENT_CLONE notification, if the initial stop has not occurred // we call ProcessMonitor::WaitForInitialTIDStop() to wait for it. // // Right now, the above logic is in ProcessPOSIX, so we need a definition of // this function in the FreeBSD ProcessMonitor implementation even if it isn't // logically needed. // // We really should figure out what actually happens on FreeBSD and move the // Linux-specific logic out of ProcessPOSIX as needed. bool ProcessMonitor::WaitForInitialTIDStop(lldb::tid_t tid) { return true; } void ProcessMonitor::StopOpThread() { if (!m_operation_thread.IsJoinable()) return; m_operation_thread.Cancel(); m_operation_thread.Join(nullptr); m_operation_thread.Reset(); } Index: vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp (revision 319149) +++ vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp (revision 319150) @@ -1,1073 +1,1080 @@ //===-- NativeProcessNetBSD.cpp ------------------------------- -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "NativeProcessNetBSD.h" // C Includes // C++ Includes // Other libraries and framework includes #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "lldb/Core/State.h" #include "lldb/Host/HostProcess.h" #include "lldb/Host/common/NativeBreakpoint.h" #include "lldb/Host/common/NativeRegisterContext.h" #include "lldb/Host/posix/ProcessLauncherPosixFork.h" #include "lldb/Target/Process.h" // System includes - They have to be included after framework includes because // they define some // macros which collide with variable names in other modules // clang-format off #include #include #include #include #include #include #include // clang-format on using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_netbsd; using namespace llvm; static ExitType convert_pid_status_to_exit_type(int status) { if (WIFEXITED(status)) return ExitType::eExitTypeExit; else if (WIFSIGNALED(status)) return ExitType::eExitTypeSignal; else if (WIFSTOPPED(status)) return ExitType::eExitTypeStop; else { // We don't know what this is. return ExitType::eExitTypeInvalid; } } static int convert_pid_status_to_return_code(int status) { if (WIFEXITED(status)) return WEXITSTATUS(status); else if (WIFSIGNALED(status)) return WTERMSIG(status); else if (WIFSTOPPED(status)) return WSTOPSIG(status); else { // We don't know what this is. return ExitType::eExitTypeInvalid; } } // Simple helper function to ensure flags are enabled on the given file // descriptor. static Status EnsureFDFlags(int fd, int flags) { Status error; int status = fcntl(fd, F_GETFL); if (status == -1) { error.SetErrorToErrno(); return error; } if (fcntl(fd, F_SETFL, status | flags) == -1) { error.SetErrorToErrno(); return error; } return error; } // ----------------------------------------------------------------------------- // Public Static Methods // ----------------------------------------------------------------------------- Status NativeProcessProtocol::Launch( ProcessLaunchInfo &launch_info, NativeProcessProtocol::NativeDelegate &native_delegate, MainLoop &mainloop, NativeProcessProtocolSP &native_process_sp) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); Status error; // Verify the working directory is valid if one was specified. FileSpec working_dir{launch_info.GetWorkingDirectory()}; if (working_dir && (!working_dir.ResolvePath() || !llvm::sys::fs::is_directory(working_dir.GetPath()))) { error.SetErrorStringWithFormat("No such file or directory: %s", working_dir.GetCString()); return error; } // Create the NativeProcessNetBSD in launch mode. native_process_sp.reset(new NativeProcessNetBSD()); if (!native_process_sp->RegisterNativeDelegate(native_delegate)) { native_process_sp.reset(); error.SetErrorStringWithFormat("failed to register the native delegate"); return error; } error = std::static_pointer_cast(native_process_sp) ->LaunchInferior(mainloop, launch_info); if (error.Fail()) { native_process_sp.reset(); LLDB_LOG(log, "failed to launch process: {0}", error); return error; } launch_info.SetProcessID(native_process_sp->GetID()); return error; } Status NativeProcessProtocol::Attach( lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate, MainLoop &mainloop, NativeProcessProtocolSP &native_process_sp) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "pid = {0:x}", pid); // Retrieve the architecture for the running process. ArchSpec process_arch; Status error = ResolveProcessArchitecture(pid, process_arch); if (!error.Success()) return error; std::shared_ptr native_process_netbsd_sp( new NativeProcessNetBSD()); if (!native_process_netbsd_sp->RegisterNativeDelegate(native_delegate)) { error.SetErrorStringWithFormat("failed to register the native delegate"); return error; } native_process_netbsd_sp->AttachToInferior(mainloop, pid, error); if (!error.Success()) return error; native_process_sp = native_process_netbsd_sp; return error; } // ----------------------------------------------------------------------------- // Public Instance Methods // ----------------------------------------------------------------------------- NativeProcessNetBSD::NativeProcessNetBSD() : NativeProcessProtocol(LLDB_INVALID_PROCESS_ID), m_arch(), m_supports_mem_region(eLazyBoolCalculate), m_mem_region_cache() {} // Handles all waitpid events from the inferior process. void NativeProcessNetBSD::MonitorCallback(lldb::pid_t pid, int signal) { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - switch (signal) { case SIGTRAP: return MonitorSIGTRAP(pid); case SIGSTOP: return MonitorSIGSTOP(pid); default: return MonitorSignal(pid, signal); } } void NativeProcessNetBSD::MonitorExited(lldb::pid_t pid, int signal, int status) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "got exit signal({0}) , pid = {1}", signal, pid); /* Stop Tracking All Threads attached to Process */ m_threads.clear(); SetExitStatus(convert_pid_status_to_exit_type(status), convert_pid_status_to_return_code(status), nullptr, true); // Notify delegate that our process has exited. SetState(StateType::eStateExited, true); } void NativeProcessNetBSD::MonitorSIGSTOP(lldb::pid_t pid) { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); ptrace_siginfo_t info; const auto siginfo_err = PtraceWrapper(PT_GET_SIGINFO, pid, &info, sizeof(info)); // Get details on the signal raised. if (siginfo_err.Success()) { // Handle SIGSTOP from LLGS (LLDB GDB Server) if (info.psi_siginfo.si_code == SI_USER && info.psi_siginfo.si_pid == ::getpid()) { /* Stop Tracking All Threads attached to Process */ for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp)->SetStoppedBySignal( SIGSTOP, &info.psi_siginfo); } } } } void NativeProcessNetBSD::MonitorSIGTRAP(lldb::pid_t pid) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); ptrace_siginfo_t info; const auto siginfo_err = PtraceWrapper(PT_GET_SIGINFO, pid, &info, sizeof(info)); // Get details on the signal raised. if (siginfo_err.Fail()) { return; } switch (info.psi_siginfo.si_code) { case TRAP_BRKPT: for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp) ->SetStoppedByBreakpoint(); FixupBreakpointPCAsNeeded( *static_pointer_cast(thread_sp)); } SetState(StateType::eStateStopped, true); break; case TRAP_TRACE: for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp)->SetStoppedByTrace(); } SetState(StateType::eStateStopped, true); break; case TRAP_EXEC: { Status error = ReinitializeThreads(); if (error.Fail()) { SetState(StateType::eStateInvalid); return; } // Let our delegate know we have just exec'd. NotifyDidExec(); for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp)->SetStoppedByExec(); } SetState(StateType::eStateStopped, true); } break; case TRAP_DBREG: { // If a watchpoint was hit, report it uint32_t wp_index; Status error = static_pointer_cast(m_threads[info.psi_lwpid]) ->GetRegisterContext() ->GetWatchpointHitIndex(wp_index, (uintptr_t)info.psi_siginfo.si_addr); if (error.Fail()) LLDB_LOG(log, "received error while checking for watchpoint hits, pid = " "{0}, LWP = {1}, error = {2}", GetID(), info.psi_lwpid, error); if (wp_index != LLDB_INVALID_INDEX32) { for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp) ->SetStoppedByWatchpoint(wp_index); } SetState(StateType::eStateStopped, true); break; } // If a breakpoint was hit, report it uint32_t bp_index; error = static_pointer_cast(m_threads[info.psi_lwpid]) ->GetRegisterContext() ->GetHardwareBreakHitIndex(bp_index, (uintptr_t)info.psi_siginfo.si_addr); if (error.Fail()) LLDB_LOG(log, "received error while checking for hardware " "breakpoint hits, pid = {0}, LWP = {1}, error = {2}", GetID(), info.psi_lwpid, error); if (bp_index != LLDB_INVALID_INDEX32) { for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp) ->SetStoppedByBreakpoint(); } SetState(StateType::eStateStopped, true); break; } } break; } } void NativeProcessNetBSD::MonitorSignal(lldb::pid_t pid, int signal) { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - ptrace_siginfo_t info; const auto siginfo_err = PtraceWrapper(PT_GET_SIGINFO, pid, &info, sizeof(info)); for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp)->SetStoppedBySignal( info.psi_siginfo.si_signo, &info.psi_siginfo); } SetState(StateType::eStateStopped, true); } Status NativeProcessNetBSD::PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data, int *result) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); Status error; int ret; errno = 0; ret = ptrace(req, static_cast<::pid_t>(pid), addr, data); if (ret == -1) error.SetErrorToErrno(); if (result) *result = ret; LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3})={4:x}", req, pid, addr, data, ret); if (error.Fail()) LLDB_LOG(log, "ptrace() failed: {0}", error); return error; } Status NativeProcessNetBSD::GetSoftwareBreakpointPCOffset( uint32_t &actual_opcode_size) { // FIXME put this behind a breakpoint protocol class that can be // set per architecture. Need ARM, MIPS support here. static const uint8_t g_i386_opcode[] = {0xCC}; switch (m_arch.GetMachine()) { case llvm::Triple::x86_64: actual_opcode_size = static_cast(sizeof(g_i386_opcode)); return Status(); default: assert(false && "CPU type not supported!"); return Status("CPU type not supported"); } } Status NativeProcessNetBSD::FixupBreakpointPCAsNeeded(NativeThreadNetBSD &thread) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS)); Status error; // Find out the size of a breakpoint (might depend on where we are in the // code). NativeRegisterContextSP context_sp = thread.GetRegisterContext(); if (!context_sp) { error.SetErrorString("cannot get a NativeRegisterContext for the thread"); LLDB_LOG(log, "failed: {0}", error); return error; } uint32_t breakpoint_size = 0; error = GetSoftwareBreakpointPCOffset(breakpoint_size); if (error.Fail()) { LLDB_LOG(log, "GetBreakpointSize() failed: {0}", error); return error; } else LLDB_LOG(log, "breakpoint size: {0}", breakpoint_size); // First try probing for a breakpoint at a software breakpoint location: PC // - breakpoint size. const lldb::addr_t initial_pc_addr = context_sp->GetPCfromBreakpointLocation(); lldb::addr_t breakpoint_addr = initial_pc_addr; if (breakpoint_size > 0) { // Do not allow breakpoint probe to wrap around. if (breakpoint_addr >= breakpoint_size) breakpoint_addr -= breakpoint_size; } // Check if we stopped because of a breakpoint. NativeBreakpointSP breakpoint_sp; error = m_breakpoint_list.GetBreakpoint(breakpoint_addr, breakpoint_sp); if (!error.Success() || !breakpoint_sp) { // We didn't find one at a software probe location. Nothing to do. LLDB_LOG(log, "pid {0} no lldb breakpoint found at current pc with " "adjustment: {1}", GetID(), breakpoint_addr); return Status(); } // If the breakpoint is not a software breakpoint, nothing to do. if (!breakpoint_sp->IsSoftwareBreakpoint()) { LLDB_LOG( log, "pid {0} breakpoint found at {1:x}, not software, nothing to adjust", GetID(), breakpoint_addr); return Status(); } // // We have a software breakpoint and need to adjust the PC. // // Sanity check. if (breakpoint_size == 0) { // Nothing to do! How did we get here? LLDB_LOG(log, "pid {0} breakpoint found at {1:x}, it is software, but the " "size is zero, nothing to do (unexpected)", GetID(), breakpoint_addr); return Status(); } // // We have a software breakpoint and need to adjust the PC. // // Sanity check. if (breakpoint_size == 0) { // Nothing to do! How did we get here? LLDB_LOG(log, "pid {0} breakpoint found at {1:x}, it is software, but the " "size is zero, nothing to do (unexpected)", GetID(), breakpoint_addr); return Status(); } // Change the program counter. LLDB_LOG(log, "pid {0} tid {1}: changing PC from {2:x} to {3:x}", GetID(), thread.GetID(), initial_pc_addr, breakpoint_addr); error = context_sp->SetPC(breakpoint_addr); if (error.Fail()) { LLDB_LOG(log, "pid {0} tid {1}: failed to set PC: {2}", GetID(), thread.GetID(), error); return error; } return error; } Status NativeProcessNetBSD::Resume(const ResumeActionList &resume_actions) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "pid {0}", GetID()); const auto &thread_sp = m_threads[0]; const ResumeAction *const action = resume_actions.GetActionForThread(thread_sp->GetID(), true); if (action == nullptr) { LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(), thread_sp->GetID()); return Status(); } Status error; switch (action->state) { case eStateRunning: { // Run the thread, possibly feeding it the signal. error = NativeProcessNetBSD::PtraceWrapper(PT_CONTINUE, GetID(), (void *)1, action->signal); if (!error.Success()) return error; for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp)->SetRunning(); } SetState(eStateRunning, true); break; } case eStateStepping: // Run the thread, possibly feeding it the signal. error = NativeProcessNetBSD::PtraceWrapper(PT_STEP, GetID(), (void *)1, action->signal); if (!error.Success()) return error; for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp)->SetStepping(); } SetState(eStateStepping, true); break; case eStateSuspended: case eStateStopped: llvm_unreachable("Unexpected state"); default: return Status("NativeProcessNetBSD::%s (): unexpected state %s specified " "for pid %" PRIu64 ", tid %" PRIu64, __FUNCTION__, StateAsCString(action->state), GetID(), thread_sp->GetID()); } return Status(); } Status NativeProcessNetBSD::Halt() { Status error; if (kill(GetID(), SIGSTOP) != 0) error.SetErrorToErrno(); return error; } Status NativeProcessNetBSD::Detach() { Status error; // Stop monitoring the inferior. m_sigchld_handle.reset(); // Tell ptrace to detach from the process. if (GetID() == LLDB_INVALID_PROCESS_ID) return error; return PtraceWrapper(PT_DETACH, GetID()); } Status NativeProcessNetBSD::Signal(int signo) { Status error; if (kill(GetID(), signo)) error.SetErrorToErrno(); return error; } Status NativeProcessNetBSD::Kill() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "pid {0}", GetID()); Status error; switch (m_state) { case StateType::eStateInvalid: case StateType::eStateExited: case StateType::eStateCrashed: case StateType::eStateDetached: case StateType::eStateUnloaded: // Nothing to do - the process is already dead. LLDB_LOG(log, "ignored for PID {0} due to current state: {1}", GetID(), StateAsCString(m_state)); return error; case StateType::eStateConnected: case StateType::eStateAttaching: case StateType::eStateLaunching: case StateType::eStateStopped: case StateType::eStateRunning: case StateType::eStateStepping: case StateType::eStateSuspended: // We can try to kill a process in these states. break; } if (kill(GetID(), SIGKILL) != 0) { error.SetErrorToErrno(); return error; } return error; } Status NativeProcessNetBSD::GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info) { if (m_supports_mem_region == LazyBool::eLazyBoolNo) { // We're done. return Status("unsupported"); } Status error = PopulateMemoryRegionCache(); if (error.Fail()) { return error; } lldb::addr_t prev_base_address = 0; // FIXME start by finding the last region that is <= target address using // binary search. Data is sorted. // There can be a ton of regions on pthreads apps with lots of threads. for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end(); ++it) { MemoryRegionInfo &proc_entry_info = it->first; // Sanity check assumption that memory map entries are ascending. assert((proc_entry_info.GetRange().GetRangeBase() >= prev_base_address) && "descending memory map entries detected, unexpected"); prev_base_address = proc_entry_info.GetRange().GetRangeBase(); UNUSED_IF_ASSERT_DISABLED(prev_base_address); // If the target address comes before this entry, indicate distance to // next region. if (load_addr < proc_entry_info.GetRange().GetRangeBase()) { range_info.GetRange().SetRangeBase(load_addr); range_info.GetRange().SetByteSize( proc_entry_info.GetRange().GetRangeBase() - load_addr); range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); return error; } else if (proc_entry_info.GetRange().Contains(load_addr)) { // The target address is within the memory region we're processing here. range_info = proc_entry_info; return error; } // The target memory address comes somewhere after the region we just // parsed. } // If we made it here, we didn't find an entry that contained the given // address. Return the // load_addr as start and the amount of bytes betwwen load address and the // end of the memory as size. range_info.GetRange().SetRangeBase(load_addr); range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); return error; } Status NativeProcessNetBSD::PopulateMemoryRegionCache() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); // If our cache is empty, pull the latest. There should always be at least // one memory region if memory region handling is supported. if (!m_mem_region_cache.empty()) { LLDB_LOG(log, "reusing {0} cached memory region entries", m_mem_region_cache.size()); return Status(); } struct kinfo_vmentry *vm; size_t count, i; vm = kinfo_getvmmap(GetID(), &count); if (vm == NULL) { m_supports_mem_region = LazyBool::eLazyBoolNo; Status error; error.SetErrorString("not supported"); return error; } for (i = 0; i < count; i++) { MemoryRegionInfo info; info.Clear(); info.GetRange().SetRangeBase(vm[i].kve_start); info.GetRange().SetRangeEnd(vm[i].kve_end); info.SetMapped(MemoryRegionInfo::OptionalBool::eYes); if (vm[i].kve_protection & VM_PROT_READ) info.SetReadable(MemoryRegionInfo::OptionalBool::eYes); else info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); if (vm[i].kve_protection & VM_PROT_WRITE) info.SetWritable(MemoryRegionInfo::OptionalBool::eYes); else info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); if (vm[i].kve_protection & VM_PROT_EXECUTE) info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes); else info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); if (vm[i].kve_path[0]) info.SetName(vm[i].kve_path); m_mem_region_cache.emplace_back( info, FileSpec(info.GetName().GetCString(), true)); } free(vm); if (m_mem_region_cache.empty()) { // No entries after attempting to read them. This shouldn't happen. // Assume we don't support map entries. LLDB_LOG(log, "failed to find any vmmap entries, assuming no support " "for memory region metadata retrieval"); m_supports_mem_region = LazyBool::eLazyBoolNo; Status error; error.SetErrorString("not supported"); return error; } LLDB_LOG(log, "read {0} memory region entries from process {1}", m_mem_region_cache.size(), GetID()); // We support memory retrieval, remember that. m_supports_mem_region = LazyBool::eLazyBoolYes; return Status(); } Status NativeProcessNetBSD::AllocateMemory(size_t size, uint32_t permissions, lldb::addr_t &addr) { return Status("Unimplemented"); } Status NativeProcessNetBSD::DeallocateMemory(lldb::addr_t addr) { return Status("Unimplemented"); } lldb::addr_t NativeProcessNetBSD::GetSharedLibraryInfoAddress() { // punt on this for now return LLDB_INVALID_ADDRESS; } size_t NativeProcessNetBSD::UpdateThreads() { return m_threads.size(); } bool NativeProcessNetBSD::GetArchitecture(ArchSpec &arch) const { arch = m_arch; return true; } Status NativeProcessNetBSD::SetBreakpoint(lldb::addr_t addr, uint32_t size, bool hardware) { if (hardware) return Status("NativeProcessNetBSD does not support hardware breakpoints"); else return SetSoftwareBreakpoint(addr, size); } Status NativeProcessNetBSD::GetSoftwareBreakpointTrapOpcode( size_t trap_opcode_size_hint, size_t &actual_opcode_size, const uint8_t *&trap_opcode_bytes) { static const uint8_t g_i386_opcode[] = {0xCC}; switch (m_arch.GetMachine()) { case llvm::Triple::x86: case llvm::Triple::x86_64: trap_opcode_bytes = g_i386_opcode; actual_opcode_size = sizeof(g_i386_opcode); return Status(); default: assert(false && "CPU type not supported!"); return Status("CPU type not supported"); } } Status NativeProcessNetBSD::GetLoadedModuleFileSpec(const char *module_path, FileSpec &file_spec) { return Status("Unimplemented"); } Status NativeProcessNetBSD::GetFileLoadAddress(const llvm::StringRef &file_name, lldb::addr_t &load_addr) { load_addr = LLDB_INVALID_ADDRESS; return Status(); } Status NativeProcessNetBSD::LaunchInferior(MainLoop &mainloop, ProcessLaunchInfo &launch_info) { Status error; m_sigchld_handle = mainloop.RegisterSignal( SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, error); if (!m_sigchld_handle) return error; SetState(eStateLaunching); ::pid_t pid = ProcessLauncherPosixFork() .LaunchProcess(launch_info, error) .GetProcessId(); if (error.Fail()) return error; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); // Wait for the child process to trap on its call to execve. ::pid_t wpid; int status; if ((wpid = waitpid(pid, &status, 0)) < 0) { error.SetErrorToErrno(); LLDB_LOG(log, "waitpid for inferior failed with %s", error); // Mark the inferior as invalid. // FIXME this could really use a new state - eStateLaunchFailure. For // now, using eStateInvalid. SetState(StateType::eStateInvalid); return error; } assert(WIFSTOPPED(status) && (wpid == static_cast<::pid_t>(pid)) && "Could not sync with inferior process."); LLDB_LOG(log, "inferior started, now in stopped state"); // Release the master terminal descriptor and pass it off to the // NativeProcessNetBSD instance. Similarly stash the inferior pid. m_terminal_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor(); m_pid = pid; launch_info.SetProcessID(pid); if (m_terminal_fd != -1) { error = EnsureFDFlags(m_terminal_fd, O_NONBLOCK); if (error.Fail()) { LLDB_LOG(log, "inferior EnsureFDFlags failed for ensuring terminal " "O_NONBLOCK setting: {0}", error); // Mark the inferior as invalid. // FIXME this could really use a new state - eStateLaunchFailure. For // now, using eStateInvalid. SetState(StateType::eStateInvalid); return error; } } LLDB_LOG(log, "adding pid = {0}", pid); ResolveProcessArchitecture(m_pid, m_arch); error = ReinitializeThreads(); if (error.Fail()) { SetState(StateType::eStateInvalid); return error; } for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp)->SetStoppedBySignal( SIGSTOP); } /* Set process stopped */ SetState(StateType::eStateStopped); if (error.Fail()) LLDB_LOG(log, "inferior launching failed {0}", error); return error; } void NativeProcessNetBSD::AttachToInferior(MainLoop &mainloop, lldb::pid_t pid, Status &error) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "pid = {0:x}", pid); m_sigchld_handle = mainloop.RegisterSignal( SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, error); if (!m_sigchld_handle) return; error = ResolveProcessArchitecture(pid, m_arch); if (!error.Success()) return; // Set the architecture to the exe architecture. LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid, m_arch.GetArchitectureName()); m_pid = pid; SetState(eStateAttaching); Attach(pid, error); } void NativeProcessNetBSD::SigchldHandler() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); // Process all pending waitpid notifications. int status; ::pid_t wait_pid = waitpid(GetID(), &status, WALLSIG | WNOHANG); if (wait_pid == 0) return; // We are done. if (wait_pid == -1) { if (errno == EINTR) return; Status error(errno, eErrorTypePOSIX); LLDB_LOG(log, "waitpid ({0}, &status, _) failed: {1}", GetID(), error); } bool exited = false; int signal = 0; int exit_status = 0; const char *status_cstr = nullptr; if (WIFSTOPPED(status)) { signal = WSTOPSIG(status); status_cstr = "STOPPED"; } else if (WIFEXITED(status)) { exit_status = WEXITSTATUS(status); status_cstr = "EXITED"; exited = true; } else if (WIFSIGNALED(status)) { signal = WTERMSIG(status); status_cstr = "SIGNALED"; if (wait_pid == static_cast<::pid_t>(GetID())) { exited = true; exit_status = -1; } } else status_cstr = "(\?\?\?)"; LLDB_LOG(log, "waitpid ({0}, &status, _) => pid = {1}, status = {2:x} " "({3}), signal = {4}, exit_state = {5}", GetID(), wait_pid, status, status_cstr, signal, exit_status); if (exited) MonitorExited(wait_pid, signal, exit_status); else MonitorCallback(wait_pid, signal); } +bool NativeProcessNetBSD::HasThreadNoLock(lldb::tid_t thread_id) { + for (auto thread_sp : m_threads) { + assert(thread_sp && "thread list should not contain NULL threads"); + if (thread_sp->GetID() == thread_id) { + // We have this thread. + return true; + } + } + + // We don't have this thread. + return false; +} + NativeThreadNetBSDSP NativeProcessNetBSD::AddThread(lldb::tid_t thread_id) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id); assert(!HasThreadNoLock(thread_id) && "attempted to add a thread by id that already exists"); // If this is the first thread, save it as the current thread if (m_threads.empty()) SetCurrentThreadID(thread_id); auto thread_sp = std::make_shared(this, thread_id); m_threads.push_back(thread_sp); return thread_sp; } ::pid_t NativeProcessNetBSD::Attach(lldb::pid_t pid, Status &error) { - Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); - if (pid <= 1) { error.SetErrorToGenericError(); error.SetErrorString("Attaching to process 1 is not allowed."); return -1; } // Attach to the requested process. // An attach will cause the thread to stop with a SIGSTOP. error = PtraceWrapper(PT_ATTACH, pid); if (error.Fail()) return -1; int status; // Need to use WALLSIG otherwise we receive an error with errno=ECHLD // At this point we should have a thread stopped if waitpid succeeds. if ((status = waitpid(pid, NULL, WALLSIG)) < 0) return -1; m_pid = pid; /* Initialize threads */ error = ReinitializeThreads(); if (error.Fail()) { SetState(StateType::eStateInvalid); return -1; } for (const auto &thread_sp : m_threads) { static_pointer_cast(thread_sp)->SetStoppedBySignal( SIGSTOP); } // Let our process instance know the thread has stopped. SetState(StateType::eStateStopped); return pid; } Status NativeProcessNetBSD::ReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) { unsigned char *dst = static_cast(buf); struct ptrace_io_desc io; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY)); LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); bytes_read = 0; io.piod_op = PIOD_READ_D; io.piod_len = size; do { io.piod_offs = (void *)(addr + bytes_read); io.piod_addr = dst + bytes_read; Status error = NativeProcessNetBSD::PtraceWrapper(PT_IO, GetID(), &io); if (error.Fail()) return error; bytes_read = io.piod_len; io.piod_len = size - bytes_read; } while (bytes_read < size); return Status(); } Status NativeProcessNetBSD::ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) { Status error = ReadMemory(addr, buf, size, bytes_read); if (error.Fail()) return error; return m_breakpoint_list.RemoveTrapsFromBuffer(addr, buf, size); } Status NativeProcessNetBSD::WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) { const unsigned char *src = static_cast(buf); Status error; struct ptrace_io_desc io; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY)); LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); bytes_written = 0; io.piod_op = PIOD_WRITE_D; io.piod_len = size; do { - io.piod_addr = (void *)(src + bytes_written); + io.piod_addr = const_cast(static_cast(src + bytes_written)); io.piod_offs = (void *)(addr + bytes_written); Status error = NativeProcessNetBSD::PtraceWrapper(PT_IO, GetID(), &io); if (error.Fail()) return error; bytes_written = io.piod_len; io.piod_len = size - bytes_written; } while (bytes_written < size); return error; } llvm::ErrorOr> NativeProcessNetBSD::GetAuxvData() const { /* * ELF_AUX_ENTRIES is currently restricted to kernel * ( r. 1.155 specifies 15) * * ptrace(2) returns the whole AUXV including extra fiels after AT_NULL this * information isn't needed. */ size_t auxv_size = 100 * sizeof(AuxInfo); ErrorOr> buf = llvm::MemoryBuffer::getNewMemBuffer(auxv_size); - struct ptrace_io_desc io = {.piod_op = PIOD_READ_AUXV, - .piod_offs = 0, - .piod_addr = (void *)buf.get()->getBufferStart(), - .piod_len = auxv_size}; + struct ptrace_io_desc io; + io.piod_op = PIOD_READ_AUXV; + io.piod_offs = 0; + io.piod_addr = const_cast(static_cast(buf.get()->getBufferStart())); + io.piod_len = auxv_size; Status error = NativeProcessNetBSD::PtraceWrapper(PT_IO, GetID(), &io); if (error.Fail()) return std::error_code(error.GetError(), std::generic_category()); if (io.piod_len < 1) return std::error_code(ECANCELED, std::generic_category()); return buf; } Status NativeProcessNetBSD::ReinitializeThreads() { // Clear old threads m_threads.clear(); // Initialize new thread struct ptrace_lwpinfo info = {}; Status error = PtraceWrapper(PT_LWPINFO, GetID(), &info, sizeof(info)); if (error.Fail()) { return error; } // Reinitialize from scratch threads and register them in process while (info.pl_lwpid != 0) { NativeThreadNetBSDSP thread_sp = AddThread(info.pl_lwpid); error = PtraceWrapper(PT_LWPINFO, GetID(), &info, sizeof(info)); if (error.Fail()) { return error; } } return error; } Index: vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h (revision 319149) +++ vendor/lldb/dist/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h (revision 319150) @@ -1,142 +1,144 @@ //===-- NativeProcessNetBSD.h --------------------------------- -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_NativeProcessNetBSD_H_ #define liblldb_NativeProcessNetBSD_H_ // C++ Includes // Other libraries and framework includes #include "lldb/Core/ArchSpec.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Utility/FileSpec.h" #include "NativeThreadNetBSD.h" #include "lldb/Host/common/NativeProcessProtocol.h" namespace lldb_private { namespace process_netbsd { /// @class NativeProcessNetBSD /// @brief Manages communication with the inferior (debugee) process. /// /// Upon construction, this class prepares and launches an inferior process for /// debugging. /// /// Changes in the inferior process state are broadcasted. class NativeProcessNetBSD : public NativeProcessProtocol { friend Status NativeProcessProtocol::Launch( ProcessLaunchInfo &launch_info, NativeDelegate &native_delegate, MainLoop &mainloop, NativeProcessProtocolSP &process_sp); friend Status NativeProcessProtocol::Attach( lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate, MainLoop &mainloop, NativeProcessProtocolSP &process_sp); public: // --------------------------------------------------------------------- // NativeProcessProtocol Interface // --------------------------------------------------------------------- Status Resume(const ResumeActionList &resume_actions) override; Status Halt() override; Status Detach() override; Status Signal(int signo) override; Status Kill() override; Status GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info) override; Status ReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) override; Status ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) override; Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) override; Status AllocateMemory(size_t size, uint32_t permissions, lldb::addr_t &addr) override; Status DeallocateMemory(lldb::addr_t addr) override; lldb::addr_t GetSharedLibraryInfoAddress() override; size_t UpdateThreads() override; bool GetArchitecture(ArchSpec &arch) const override; Status SetBreakpoint(lldb::addr_t addr, uint32_t size, bool hardware) override; Status GetLoadedModuleFileSpec(const char *module_path, FileSpec &file_spec) override; Status GetFileLoadAddress(const llvm::StringRef &file_name, lldb::addr_t &load_addr) override; llvm::ErrorOr> GetAuxvData() const override; // --------------------------------------------------------------------- // Interface used by NativeRegisterContext-derived classes. // --------------------------------------------------------------------- static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr, int data = 0, int *result = nullptr); protected: // --------------------------------------------------------------------- // NativeProcessProtocol protected interface // --------------------------------------------------------------------- Status GetSoftwareBreakpointTrapOpcode(size_t trap_opcode_size_hint, size_t &actual_opcode_size, const uint8_t *&trap_opcode_bytes) override; private: MainLoop::SignalHandleUP m_sigchld_handle; ArchSpec m_arch; LazyBool m_supports_mem_region; std::vector> m_mem_region_cache; // --------------------------------------------------------------------- // Private Instance Methods // --------------------------------------------------------------------- NativeProcessNetBSD(); + bool HasThreadNoLock(lldb::tid_t thread_id); + NativeThreadNetBSDSP AddThread(lldb::tid_t thread_id); Status LaunchInferior(MainLoop &mainloop, ProcessLaunchInfo &launch_info); void AttachToInferior(MainLoop &mainloop, lldb::pid_t pid, Status &error); void MonitorCallback(lldb::pid_t pid, int signal); void MonitorExited(lldb::pid_t pid, int signal, int status); void MonitorSIGSTOP(lldb::pid_t pid); void MonitorSIGTRAP(lldb::pid_t pid); void MonitorSignal(lldb::pid_t pid, int signal); Status GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size); Status FixupBreakpointPCAsNeeded(NativeThreadNetBSD &thread); Status PopulateMemoryRegionCache(); void SigchldHandler(); ::pid_t Attach(lldb::pid_t pid, Status &error); Status ReinitializeThreads(); }; } // namespace process_netbsd } // namespace lldb_private #endif // #ifndef liblldb_NativeProcessNetBSD_H_ Index: vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp (revision 319149) +++ vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp (revision 319150) @@ -1,3672 +1,3876 @@ //===-- GDBRemoteCommunicationClient.cpp ------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "GDBRemoteCommunicationClient.h" // C Includes #include #include // C++ Includes #include #include // Other libraries and framework includes #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/State.h" #include "lldb/Host/HostInfo.h" #include "lldb/Interpreter/Args.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/JSON.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Log.h" -#include "lldb/Utility/StreamGDBRemote.h" #include "lldb/Utility/StreamString.h" // Project includes #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "Utility/StringExtractorGDBRemote.h" #include "lldb/Host/Config.h" #include "llvm/ADT/StringSwitch.h" #if defined(HAVE_LIBCOMPRESSION) #include #endif using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_gdb_remote; using namespace std::chrono; //---------------------------------------------------------------------- // GDBRemoteCommunicationClient constructor //---------------------------------------------------------------------- GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() : GDBRemoteClientBase("gdb-remote.client", "gdb-remote.client.rx_packet"), m_supports_not_sending_acks(eLazyBoolCalculate), m_supports_thread_suffix(eLazyBoolCalculate), m_supports_threads_in_stop_reply(eLazyBoolCalculate), m_supports_vCont_all(eLazyBoolCalculate), m_supports_vCont_any(eLazyBoolCalculate), m_supports_vCont_c(eLazyBoolCalculate), m_supports_vCont_C(eLazyBoolCalculate), m_supports_vCont_s(eLazyBoolCalculate), m_supports_vCont_S(eLazyBoolCalculate), m_qHostInfo_is_valid(eLazyBoolCalculate), m_curr_pid_is_valid(eLazyBoolCalculate), m_qProcessInfo_is_valid(eLazyBoolCalculate), m_qGDBServerVersion_is_valid(eLazyBoolCalculate), m_supports_alloc_dealloc_memory(eLazyBoolCalculate), m_supports_memory_region_info(eLazyBoolCalculate), m_supports_watchpoint_support_info(eLazyBoolCalculate), m_supports_detach_stay_stopped(eLazyBoolCalculate), m_watchpoints_trigger_after_instruction(eLazyBoolCalculate), m_attach_or_wait_reply(eLazyBoolCalculate), m_prepare_for_reg_writing_reply(eLazyBoolCalculate), m_supports_p(eLazyBoolCalculate), m_supports_x(eLazyBoolCalculate), m_avoid_g_packets(eLazyBoolCalculate), m_supports_QSaveRegisterState(eLazyBoolCalculate), m_supports_qXfer_auxv_read(eLazyBoolCalculate), m_supports_qXfer_libraries_read(eLazyBoolCalculate), m_supports_qXfer_libraries_svr4_read(eLazyBoolCalculate), m_supports_qXfer_features_read(eLazyBoolCalculate), m_supports_augmented_libraries_svr4_read(eLazyBoolCalculate), m_supports_jThreadExtendedInfo(eLazyBoolCalculate), m_supports_jLoadedDynamicLibrariesInfos(eLazyBoolCalculate), m_supports_jGetSharedCacheInfo(eLazyBoolCalculate), m_supports_QPassSignals(eLazyBoolCalculate), m_supports_qProcessInfoPID(true), m_supports_qfProcessInfo(true), m_supports_qUserName(true), m_supports_qGroupName(true), m_supports_qThreadStopInfo(true), m_supports_z0(true), m_supports_z1(true), m_supports_z2(true), m_supports_z3(true), m_supports_z4(true), m_supports_QEnvironment(true), m_supports_QEnvironmentHexEncoded(true), m_supports_qSymbol(true), m_qSymbol_requests_done(false), m_supports_qModuleInfo(true), m_supports_jThreadsInfo(true), m_supports_jModulesInfo(true), m_curr_pid(LLDB_INVALID_PROCESS_ID), m_curr_tid(LLDB_INVALID_THREAD_ID), m_curr_tid_run(LLDB_INVALID_THREAD_ID), m_num_supported_hardware_watchpoints(0), m_host_arch(), m_process_arch(), m_os_version_major(UINT32_MAX), m_os_version_minor(UINT32_MAX), m_os_version_update(UINT32_MAX), m_os_build(), m_os_kernel(), m_hostname(), m_gdb_server_name(), m_gdb_server_version(UINT32_MAX), m_default_packet_timeout(0), m_max_packet_size(0), m_qSupported_response(), m_supported_async_json_packets_is_valid(false), m_supported_async_json_packets_sp() {} //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- GDBRemoteCommunicationClient::~GDBRemoteCommunicationClient() { if (IsConnected()) Disconnect(); } bool GDBRemoteCommunicationClient::HandshakeWithServer(Status *error_ptr) { ResetDiscoverableSettings(false); // Start the read thread after we send the handshake ack since if we // fail to send the handshake ack, there is no reason to continue... if (SendAck()) { // Wait for any responses that might have been queued up in the remote // GDB server and flush them all StringExtractorGDBRemote response; PacketResult packet_result = PacketResult::Success; while (packet_result == PacketResult::Success) packet_result = ReadPacket(response, milliseconds(10), false); // The return value from QueryNoAckModeSupported() is true if the packet // was sent and _any_ response (including UNIMPLEMENTED) was received), // or false if no response was received. This quickly tells us if we have // a live connection to a remote GDB server... if (QueryNoAckModeSupported()) { return true; } else { if (error_ptr) error_ptr->SetErrorString("failed to get reply to handshake packet"); } } else { if (error_ptr) error_ptr->SetErrorString("failed to send the handshake ack"); } return false; } bool GDBRemoteCommunicationClient::GetEchoSupported() { if (m_supports_qEcho == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_qEcho == eLazyBoolYes; } bool GDBRemoteCommunicationClient::GetQPassSignalsSupported() { if (m_supports_QPassSignals == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_QPassSignals == eLazyBoolYes; } bool GDBRemoteCommunicationClient::GetAugmentedLibrariesSVR4ReadSupported() { if (m_supports_augmented_libraries_svr4_read == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_augmented_libraries_svr4_read == eLazyBoolYes; } bool GDBRemoteCommunicationClient::GetQXferLibrariesSVR4ReadSupported() { if (m_supports_qXfer_libraries_svr4_read == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_qXfer_libraries_svr4_read == eLazyBoolYes; } bool GDBRemoteCommunicationClient::GetQXferLibrariesReadSupported() { if (m_supports_qXfer_libraries_read == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_qXfer_libraries_read == eLazyBoolYes; } bool GDBRemoteCommunicationClient::GetQXferAuxvReadSupported() { if (m_supports_qXfer_auxv_read == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_qXfer_auxv_read == eLazyBoolYes; } bool GDBRemoteCommunicationClient::GetQXferFeaturesReadSupported() { if (m_supports_qXfer_features_read == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_qXfer_features_read == eLazyBoolYes; } uint64_t GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() { if (m_max_packet_size == 0) { GetRemoteQSupported(); } return m_max_packet_size; } bool GDBRemoteCommunicationClient::QueryNoAckModeSupported() { if (m_supports_not_sending_acks == eLazyBoolCalculate) { m_send_acks = true; m_supports_not_sending_acks = eLazyBoolNo; // This is the first real packet that we'll send in a debug session and it // may take a little // longer than normal to receive a reply. Wait at least 6 seconds for a // reply to this packet. ScopedTimeout timeout(*this, std::max(GetPacketTimeout(), seconds(6))); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("QStartNoAckMode", response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_send_acks = false; m_supports_not_sending_acks = eLazyBoolYes; } return true; } } return false; } void GDBRemoteCommunicationClient::GetListThreadsInStopReplySupported() { if (m_supports_threads_in_stop_reply == eLazyBoolCalculate) { m_supports_threads_in_stop_reply = eLazyBoolNo; StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("QListThreadsInStopReply", response, false) == PacketResult::Success) { if (response.IsOKResponse()) m_supports_threads_in_stop_reply = eLazyBoolYes; } } } bool GDBRemoteCommunicationClient::GetVAttachOrWaitSupported() { if (m_attach_or_wait_reply == eLazyBoolCalculate) { m_attach_or_wait_reply = eLazyBoolNo; StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qVAttachOrWaitSupported", response, false) == PacketResult::Success) { if (response.IsOKResponse()) m_attach_or_wait_reply = eLazyBoolYes; } } if (m_attach_or_wait_reply == eLazyBoolYes) return true; else return false; } bool GDBRemoteCommunicationClient::GetSyncThreadStateSupported() { if (m_prepare_for_reg_writing_reply == eLazyBoolCalculate) { m_prepare_for_reg_writing_reply = eLazyBoolNo; StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qSyncThreadStateSupported", response, false) == PacketResult::Success) { if (response.IsOKResponse()) m_prepare_for_reg_writing_reply = eLazyBoolYes; } } if (m_prepare_for_reg_writing_reply == eLazyBoolYes) return true; else return false; } void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) { if (did_exec == false) { // Hard reset everything, this is when we first connect to a GDB server m_supports_not_sending_acks = eLazyBoolCalculate; m_supports_thread_suffix = eLazyBoolCalculate; m_supports_threads_in_stop_reply = eLazyBoolCalculate; m_supports_vCont_c = eLazyBoolCalculate; m_supports_vCont_C = eLazyBoolCalculate; m_supports_vCont_s = eLazyBoolCalculate; m_supports_vCont_S = eLazyBoolCalculate; m_supports_p = eLazyBoolCalculate; m_supports_x = eLazyBoolCalculate; m_supports_QSaveRegisterState = eLazyBoolCalculate; m_qHostInfo_is_valid = eLazyBoolCalculate; m_curr_pid_is_valid = eLazyBoolCalculate; m_qGDBServerVersion_is_valid = eLazyBoolCalculate; m_supports_alloc_dealloc_memory = eLazyBoolCalculate; m_supports_memory_region_info = eLazyBoolCalculate; m_prepare_for_reg_writing_reply = eLazyBoolCalculate; m_attach_or_wait_reply = eLazyBoolCalculate; m_avoid_g_packets = eLazyBoolCalculate; m_supports_qXfer_auxv_read = eLazyBoolCalculate; m_supports_qXfer_libraries_read = eLazyBoolCalculate; m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate; m_supports_qXfer_features_read = eLazyBoolCalculate; m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate; m_supports_qProcessInfoPID = true; m_supports_qfProcessInfo = true; m_supports_qUserName = true; m_supports_qGroupName = true; m_supports_qThreadStopInfo = true; m_supports_z0 = true; m_supports_z1 = true; m_supports_z2 = true; m_supports_z3 = true; m_supports_z4 = true; m_supports_QEnvironment = true; m_supports_QEnvironmentHexEncoded = true; m_supports_qSymbol = true; m_qSymbol_requests_done = false; m_supports_qModuleInfo = true; m_host_arch.Clear(); m_os_version_major = UINT32_MAX; m_os_version_minor = UINT32_MAX; m_os_version_update = UINT32_MAX; m_os_build.clear(); m_os_kernel.clear(); m_hostname.clear(); m_gdb_server_name.clear(); m_gdb_server_version = UINT32_MAX; m_default_packet_timeout = seconds(0); m_max_packet_size = 0; m_qSupported_response.clear(); m_supported_async_json_packets_is_valid = false; m_supported_async_json_packets_sp.reset(); m_supports_jModulesInfo = true; } // These flags should be reset when we first connect to a GDB server // and when our inferior process execs m_qProcessInfo_is_valid = eLazyBoolCalculate; m_process_arch.Clear(); } void GDBRemoteCommunicationClient::GetRemoteQSupported() { // Clear out any capabilities we expect to see in the qSupported response m_supports_qXfer_auxv_read = eLazyBoolNo; m_supports_qXfer_libraries_read = eLazyBoolNo; m_supports_qXfer_libraries_svr4_read = eLazyBoolNo; m_supports_augmented_libraries_svr4_read = eLazyBoolNo; m_supports_qXfer_features_read = eLazyBoolNo; m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if // not, we assume no limit // build the qSupported packet std::vector features = {"xmlRegisters=i386,arm,mips"}; StreamString packet; packet.PutCString("qSupported"); for (uint32_t i = 0; i < features.size(); ++i) { packet.PutCString(i == 0 ? ":" : ";"); packet.PutCString(features[i]); } StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, /*send_async=*/false) == PacketResult::Success) { const char *response_cstr = response.GetStringRef().c_str(); // Hang on to the qSupported packet, so that platforms can do custom // configuration of the transport before attaching/launching the // process. m_qSupported_response = response_cstr; if (::strstr(response_cstr, "qXfer:auxv:read+")) m_supports_qXfer_auxv_read = eLazyBoolYes; if (::strstr(response_cstr, "qXfer:libraries-svr4:read+")) m_supports_qXfer_libraries_svr4_read = eLazyBoolYes; if (::strstr(response_cstr, "augmented-libraries-svr4-read")) { m_supports_qXfer_libraries_svr4_read = eLazyBoolYes; // implied m_supports_augmented_libraries_svr4_read = eLazyBoolYes; } if (::strstr(response_cstr, "qXfer:libraries:read+")) m_supports_qXfer_libraries_read = eLazyBoolYes; if (::strstr(response_cstr, "qXfer:features:read+")) m_supports_qXfer_features_read = eLazyBoolYes; // Look for a list of compressions in the features list e.g. // qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-deflate,lzma const char *features_list = ::strstr(response_cstr, "qXfer:features:"); if (features_list) { const char *compressions = ::strstr(features_list, "SupportedCompressions="); if (compressions) { std::vector supported_compressions; compressions += sizeof("SupportedCompressions=") - 1; const char *end_of_compressions = strchr(compressions, ';'); if (end_of_compressions == NULL) { end_of_compressions = strchr(compressions, '\0'); } const char *current_compression = compressions; while (current_compression < end_of_compressions) { const char *next_compression_name = strchr(current_compression, ','); const char *end_of_this_word = next_compression_name; if (next_compression_name == NULL || end_of_compressions < next_compression_name) { end_of_this_word = end_of_compressions; } if (end_of_this_word) { if (end_of_this_word == current_compression) { current_compression++; } else { std::string this_compression( current_compression, end_of_this_word - current_compression); supported_compressions.push_back(this_compression); current_compression = end_of_this_word + 1; } } else { supported_compressions.push_back(current_compression); current_compression = end_of_compressions; } } if (supported_compressions.size() > 0) { MaybeEnableCompression(supported_compressions); } } } if (::strstr(response_cstr, "qEcho")) m_supports_qEcho = eLazyBoolYes; else m_supports_qEcho = eLazyBoolNo; if (::strstr(response_cstr, "QPassSignals+")) m_supports_QPassSignals = eLazyBoolYes; else m_supports_QPassSignals = eLazyBoolNo; const char *packet_size_str = ::strstr(response_cstr, "PacketSize="); if (packet_size_str) { StringExtractorGDBRemote packet_response(packet_size_str + strlen("PacketSize=")); m_max_packet_size = packet_response.GetHexMaxU64(/*little_endian=*/false, UINT64_MAX); if (m_max_packet_size == 0) { m_max_packet_size = UINT64_MAX; // Must have been a garbled response Log *log( ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("Garbled PacketSize spec in qSupported response"); } } } } bool GDBRemoteCommunicationClient::GetThreadSuffixSupported() { if (m_supports_thread_suffix == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_thread_suffix = eLazyBoolNo; if (SendPacketAndWaitForResponse("QThreadSuffixSupported", response, false) == PacketResult::Success) { if (response.IsOKResponse()) m_supports_thread_suffix = eLazyBoolYes; } } return m_supports_thread_suffix; } bool GDBRemoteCommunicationClient::GetVContSupported(char flavor) { if (m_supports_vCont_c == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_vCont_any = eLazyBoolNo; m_supports_vCont_all = eLazyBoolNo; m_supports_vCont_c = eLazyBoolNo; m_supports_vCont_C = eLazyBoolNo; m_supports_vCont_s = eLazyBoolNo; m_supports_vCont_S = eLazyBoolNo; if (SendPacketAndWaitForResponse("vCont?", response, false) == PacketResult::Success) { const char *response_cstr = response.GetStringRef().c_str(); if (::strstr(response_cstr, ";c")) m_supports_vCont_c = eLazyBoolYes; if (::strstr(response_cstr, ";C")) m_supports_vCont_C = eLazyBoolYes; if (::strstr(response_cstr, ";s")) m_supports_vCont_s = eLazyBoolYes; if (::strstr(response_cstr, ";S")) m_supports_vCont_S = eLazyBoolYes; if (m_supports_vCont_c == eLazyBoolYes && m_supports_vCont_C == eLazyBoolYes && m_supports_vCont_s == eLazyBoolYes && m_supports_vCont_S == eLazyBoolYes) { m_supports_vCont_all = eLazyBoolYes; } if (m_supports_vCont_c == eLazyBoolYes || m_supports_vCont_C == eLazyBoolYes || m_supports_vCont_s == eLazyBoolYes || m_supports_vCont_S == eLazyBoolYes) { m_supports_vCont_any = eLazyBoolYes; } } } switch (flavor) { case 'a': return m_supports_vCont_any; case 'A': return m_supports_vCont_all; case 'c': return m_supports_vCont_c; case 'C': return m_supports_vCont_C; case 's': return m_supports_vCont_s; case 'S': return m_supports_vCont_S; default: break; } return false; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationClient::SendThreadSpecificPacketAndWaitForResponse( lldb::tid_t tid, StreamString &&payload, StringExtractorGDBRemote &response, bool send_async) { Lock lock(*this, send_async); if (!lock) { if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet( GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)) log->Printf("GDBRemoteCommunicationClient::%s: Didn't get sequence mutex " "for %s packet.", __FUNCTION__, payload.GetData()); return PacketResult::ErrorNoSequenceLock; } if (GetThreadSuffixSupported()) payload.Printf(";thread:%4.4" PRIx64 ";", tid); else { if (!SetCurrentThread(tid)) return PacketResult::ErrorSendFailed; } return SendPacketAndWaitForResponseNoLock(payload.GetString(), response); } // Check if the target supports 'p' packet. It sends out a 'p' // packet and checks the response. A normal packet will tell us // that support is available. // // Takes a valid thread ID because p needs to apply to a thread. bool GDBRemoteCommunicationClient::GetpPacketSupported(lldb::tid_t tid) { if (m_supports_p == eLazyBoolCalculate) { m_supports_p = eLazyBoolNo; StreamString payload; payload.PutCString("p0"); StringExtractorGDBRemote response; if (SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload), response, false) == PacketResult::Success && response.IsNormalResponse()) { m_supports_p = eLazyBoolYes; } } return m_supports_p; } StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() { // Get information on all threads at one using the "jThreadsInfo" packet StructuredData::ObjectSP object_sp; if (m_supports_jThreadsInfo) { StringExtractorGDBRemote response; response.SetResponseValidatorToJSON(); if (SendPacketAndWaitForResponse("jThreadsInfo", response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) { m_supports_jThreadsInfo = false; } else if (!response.Empty()) { object_sp = StructuredData::ParseJSON(response.GetStringRef()); } } } return object_sp; } bool GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported() { if (m_supports_jThreadExtendedInfo == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_jThreadExtendedInfo = eLazyBoolNo; if (SendPacketAndWaitForResponse("jThreadExtendedInfo:", response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_supports_jThreadExtendedInfo = eLazyBoolYes; } } } return m_supports_jThreadExtendedInfo; } bool GDBRemoteCommunicationClient::GetLoadedDynamicLibrariesInfosSupported() { if (m_supports_jLoadedDynamicLibrariesInfos == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolNo; if (SendPacketAndWaitForResponse("jGetLoadedDynamicLibrariesInfos:", response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolYes; } } } return m_supports_jLoadedDynamicLibrariesInfos; } bool GDBRemoteCommunicationClient::GetSharedCacheInfoSupported() { if (m_supports_jGetSharedCacheInfo == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_jGetSharedCacheInfo = eLazyBoolNo; if (SendPacketAndWaitForResponse("jGetSharedCacheInfo:", response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_supports_jGetSharedCacheInfo = eLazyBoolYes; } } } return m_supports_jGetSharedCacheInfo; } bool GDBRemoteCommunicationClient::GetxPacketSupported() { if (m_supports_x == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_x = eLazyBoolNo; char packet[256]; snprintf(packet, sizeof(packet), "x0,0"); if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsOKResponse()) m_supports_x = eLazyBoolYes; } } return m_supports_x; } GDBRemoteCommunicationClient::PacketResult GDBRemoteCommunicationClient::SendPacketsAndConcatenateResponses( const char *payload_prefix, std::string &response_string) { Lock lock(*this, false); if (!lock) { Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); if (log) log->Printf("error: failed to get packet sequence mutex, not sending " "packets with prefix '%s'", payload_prefix); return PacketResult::ErrorNoSequenceLock; } response_string = ""; std::string payload_prefix_str(payload_prefix); unsigned int response_size = 0x1000; if (response_size > GetRemoteMaxPacketSize()) { // May send qSupported packet response_size = GetRemoteMaxPacketSize(); } for (unsigned int offset = 0; true; offset += response_size) { StringExtractorGDBRemote this_response; // Construct payload char sizeDescriptor[128]; snprintf(sizeDescriptor, sizeof(sizeDescriptor), "%x,%x", offset, response_size); PacketResult result = SendPacketAndWaitForResponseNoLock( payload_prefix_str + sizeDescriptor, this_response); if (result != PacketResult::Success) return result; const std::string &this_string = this_response.GetStringRef(); // Check for m or l as first character; l seems to mean this is the last // chunk char first_char = *this_string.c_str(); if (first_char != 'm' && first_char != 'l') { return PacketResult::ErrorReplyInvalid; } // Concatenate the result so far (skipping 'm' or 'l') response_string.append(this_string, 1, std::string::npos); if (first_char == 'l') // We're done return PacketResult::Success; } } lldb::pid_t GDBRemoteCommunicationClient::GetCurrentProcessID(bool allow_lazy) { if (allow_lazy && m_curr_pid_is_valid == eLazyBoolYes) return m_curr_pid; // First try to retrieve the pid via the qProcessInfo request. GetCurrentProcessInfo(allow_lazy); if (m_curr_pid_is_valid == eLazyBoolYes) { // We really got it. return m_curr_pid; } else { // If we don't get a response for qProcessInfo, check if $qC gives us a // result. // $qC only returns a real process id on older debugserver and lldb-platform // stubs. // The gdb remote protocol documents $qC as returning the thread id, which // newer // debugserver and lldb-gdbserver stubs return correctly. StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qC", response, false) == PacketResult::Success) { if (response.GetChar() == 'Q') { if (response.GetChar() == 'C') { m_curr_pid = response.GetHexMaxU32(false, LLDB_INVALID_PROCESS_ID); if (m_curr_pid != LLDB_INVALID_PROCESS_ID) { m_curr_pid_is_valid = eLazyBoolYes; return m_curr_pid; } } } } // If we don't get a response for $qC, check if $qfThreadID gives us a // result. if (m_curr_pid == LLDB_INVALID_PROCESS_ID) { std::vector thread_ids; bool sequence_mutex_unavailable; size_t size; size = GetCurrentThreadIDs(thread_ids, sequence_mutex_unavailable); if (size && sequence_mutex_unavailable == false) { m_curr_pid = thread_ids.front(); m_curr_pid_is_valid = eLazyBoolYes; return m_curr_pid; } } } return LLDB_INVALID_PROCESS_ID; } bool GDBRemoteCommunicationClient::GetLaunchSuccess(std::string &error_str) { error_str.clear(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qLaunchSuccess", response, false) == PacketResult::Success) { if (response.IsOKResponse()) return true; if (response.GetChar() == 'E') { // A string the describes what failed when launching... error_str = response.GetStringRef().substr(1); } else { error_str.assign("unknown error occurred launching process"); } } else { error_str.assign("timed out waiting for app to launch"); } return false; } int GDBRemoteCommunicationClient::SendArgumentsPacket( const ProcessLaunchInfo &launch_info) { // Since we don't get the send argv0 separate from the executable path, we // need to // make sure to use the actual executable path found in the launch_info... std::vector argv; FileSpec exe_file = launch_info.GetExecutableFile(); std::string exe_path; const char *arg = NULL; const Args &launch_args = launch_info.GetArguments(); if (exe_file) exe_path = exe_file.GetPath(false); else { arg = launch_args.GetArgumentAtIndex(0); if (arg) exe_path = arg; } if (!exe_path.empty()) { argv.push_back(exe_path.c_str()); for (uint32_t i = 1; (arg = launch_args.GetArgumentAtIndex(i)) != NULL; ++i) { if (arg) argv.push_back(arg); } } if (!argv.empty()) { StreamString packet; packet.PutChar('A'); for (size_t i = 0, n = argv.size(); i < n; ++i) { arg = argv[i]; const int arg_len = strlen(arg); if (i > 0) packet.PutChar(','); packet.Printf("%i,%i,", arg_len * 2, (int)i); packet.PutBytesAsRawHex8(arg, arg_len); } StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } int GDBRemoteCommunicationClient::SendEnvironmentPacket( char const *name_equal_value) { if (name_equal_value && name_equal_value[0]) { StreamString packet; bool send_hex_encoding = false; for (const char *p = name_equal_value; *p != '\0' && send_hex_encoding == false; ++p) { if (isprint(*p)) { switch (*p) { case '$': case '#': case '*': case '}': send_hex_encoding = true; break; default: break; } } else { // We have non printable characters, lets hex encode this... send_hex_encoding = true; } } StringExtractorGDBRemote response; if (send_hex_encoding) { if (m_supports_QEnvironmentHexEncoded) { packet.PutCString("QEnvironmentHexEncoded:"); packet.PutBytesAsRawHex8(name_equal_value, strlen(name_equal_value)); if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; if (response.IsUnsupportedResponse()) m_supports_QEnvironmentHexEncoded = false; } } } else if (m_supports_QEnvironment) { packet.Printf("QEnvironment:%s", name_equal_value); if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; if (response.IsUnsupportedResponse()) m_supports_QEnvironment = false; } } } return -1; } int GDBRemoteCommunicationClient::SendLaunchArchPacket(char const *arch) { if (arch && arch[0]) { StreamString packet; packet.Printf("QLaunchArch:%s", arch); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } int GDBRemoteCommunicationClient::SendLaunchEventDataPacket( char const *data, bool *was_supported) { if (data && *data != '\0') { StreamString packet; packet.Printf("QSetProcessEvent:%s", data); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) { if (was_supported) *was_supported = true; return 0; } else if (response.IsUnsupportedResponse()) { if (was_supported) *was_supported = false; return -1; } else { uint8_t error = response.GetError(); if (was_supported) *was_supported = true; if (error) return error; } } } return -1; } bool GDBRemoteCommunicationClient::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) { if (GetHostInfo()) { if (m_os_version_major != UINT32_MAX) { major = m_os_version_major; minor = m_os_version_minor; update = m_os_version_update; return true; } } return false; } bool GDBRemoteCommunicationClient::GetOSBuildString(std::string &s) { if (GetHostInfo()) { if (!m_os_build.empty()) { s = m_os_build; return true; } } s.clear(); return false; } bool GDBRemoteCommunicationClient::GetOSKernelDescription(std::string &s) { if (GetHostInfo()) { if (!m_os_kernel.empty()) { s = m_os_kernel; return true; } } s.clear(); return false; } bool GDBRemoteCommunicationClient::GetHostname(std::string &s) { if (GetHostInfo()) { if (!m_hostname.empty()) { s = m_hostname; return true; } } s.clear(); return false; } ArchSpec GDBRemoteCommunicationClient::GetSystemArchitecture() { if (GetHostInfo()) return m_host_arch; return ArchSpec(); } const lldb_private::ArchSpec & GDBRemoteCommunicationClient::GetProcessArchitecture() { if (m_qProcessInfo_is_valid == eLazyBoolCalculate) GetCurrentProcessInfo(); return m_process_arch; } bool GDBRemoteCommunicationClient::GetGDBServerVersion() { if (m_qGDBServerVersion_is_valid == eLazyBoolCalculate) { m_gdb_server_name.clear(); m_gdb_server_version = 0; m_qGDBServerVersion_is_valid = eLazyBoolNo; StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qGDBServerVersion", response, false) == PacketResult::Success) { if (response.IsNormalResponse()) { llvm::StringRef name, value; bool success = false; while (response.GetNameColonValue(name, value)) { if (name.equals("name")) { success = true; m_gdb_server_name = value; } else if (name.equals("version")) { llvm::StringRef major, minor; std::tie(major, minor) = value.split('.'); if (!major.getAsInteger(0, m_gdb_server_version)) success = true; } } if (success) m_qGDBServerVersion_is_valid = eLazyBoolYes; } } } return m_qGDBServerVersion_is_valid == eLazyBoolYes; } void GDBRemoteCommunicationClient::MaybeEnableCompression( std::vector supported_compressions) { CompressionType avail_type = CompressionType::None; std::string avail_name; #if defined(HAVE_LIBCOMPRESSION) // libcompression is weak linked so test if compression_decode_buffer() is // available if (compression_decode_buffer != NULL && avail_type == CompressionType::None) { for (auto compression : supported_compressions) { if (compression == "lzfse") { avail_type = CompressionType::LZFSE; avail_name = compression; break; } } } #endif #if defined(HAVE_LIBCOMPRESSION) // libcompression is weak linked so test if compression_decode_buffer() is // available if (compression_decode_buffer != NULL && avail_type == CompressionType::None) { for (auto compression : supported_compressions) { if (compression == "zlib-deflate") { avail_type = CompressionType::ZlibDeflate; avail_name = compression; break; } } } #endif #if defined(HAVE_LIBZ) if (avail_type == CompressionType::None) { for (auto compression : supported_compressions) { if (compression == "zlib-deflate") { avail_type = CompressionType::ZlibDeflate; avail_name = compression; break; } } } #endif #if defined(HAVE_LIBCOMPRESSION) // libcompression is weak linked so test if compression_decode_buffer() is // available if (compression_decode_buffer != NULL && avail_type == CompressionType::None) { for (auto compression : supported_compressions) { if (compression == "lz4") { avail_type = CompressionType::LZ4; avail_name = compression; break; } } } #endif #if defined(HAVE_LIBCOMPRESSION) // libcompression is weak linked so test if compression_decode_buffer() is // available if (compression_decode_buffer != NULL && avail_type == CompressionType::None) { for (auto compression : supported_compressions) { if (compression == "lzma") { avail_type = CompressionType::LZMA; avail_name = compression; break; } } } #endif if (avail_type != CompressionType::None) { StringExtractorGDBRemote response; std::string packet = "QEnableCompression:type:" + avail_name + ";"; if (SendPacketAndWaitForResponse(packet, response, false) != PacketResult::Success) return; if (response.IsOKResponse()) { m_compression_type = avail_type; } } } const char *GDBRemoteCommunicationClient::GetGDBServerProgramName() { if (GetGDBServerVersion()) { if (!m_gdb_server_name.empty()) return m_gdb_server_name.c_str(); } return NULL; } uint32_t GDBRemoteCommunicationClient::GetGDBServerProgramVersion() { if (GetGDBServerVersion()) return m_gdb_server_version; return 0; } bool GDBRemoteCommunicationClient::GetDefaultThreadId(lldb::tid_t &tid) { StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qC", response, false) != PacketResult::Success) return false; if (!response.IsNormalResponse()) return false; if (response.GetChar() == 'Q' && response.GetChar() == 'C') tid = response.GetHexMaxU32(true, -1); return true; } bool GDBRemoteCommunicationClient::GetHostInfo(bool force) { Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS)); if (force || m_qHostInfo_is_valid == eLazyBoolCalculate) { m_qHostInfo_is_valid = eLazyBoolNo; StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qHostInfo", response, false) == PacketResult::Success) { if (response.IsNormalResponse()) { llvm::StringRef name; llvm::StringRef value; uint32_t cpu = LLDB_INVALID_CPUTYPE; uint32_t sub = 0; std::string arch_name; std::string os_name; std::string vendor_name; std::string triple; std::string distribution_id; uint32_t pointer_byte_size = 0; ByteOrder byte_order = eByteOrderInvalid; uint32_t num_keys_decoded = 0; while (response.GetNameColonValue(name, value)) { if (name.equals("cputype")) { // exception type in big endian hex if (!value.getAsInteger(0, cpu)) ++num_keys_decoded; } else if (name.equals("cpusubtype")) { // exception count in big endian hex if (!value.getAsInteger(0, sub)) ++num_keys_decoded; } else if (name.equals("arch")) { arch_name = value; ++num_keys_decoded; } else if (name.equals("triple")) { StringExtractor extractor(value); extractor.GetHexByteString(triple); ++num_keys_decoded; } else if (name.equals("distribution_id")) { StringExtractor extractor(value); extractor.GetHexByteString(distribution_id); ++num_keys_decoded; } else if (name.equals("os_build")) { StringExtractor extractor(value); extractor.GetHexByteString(m_os_build); ++num_keys_decoded; } else if (name.equals("hostname")) { StringExtractor extractor(value); extractor.GetHexByteString(m_hostname); ++num_keys_decoded; } else if (name.equals("os_kernel")) { StringExtractor extractor(value); extractor.GetHexByteString(m_os_kernel); ++num_keys_decoded; } else if (name.equals("ostype")) { os_name = value; ++num_keys_decoded; } else if (name.equals("vendor")) { vendor_name = value; ++num_keys_decoded; } else if (name.equals("endian")) { byte_order = llvm::StringSwitch(value) .Case("little", eByteOrderLittle) .Case("big", eByteOrderBig) .Case("pdp", eByteOrderPDP) .Default(eByteOrderInvalid); if (byte_order != eByteOrderInvalid) ++num_keys_decoded; } else if (name.equals("ptrsize")) { if (!value.getAsInteger(0, pointer_byte_size)) ++num_keys_decoded; } else if (name.equals("os_version") || name.equals( "version")) // Older debugserver binaries used the // "version" key instead of // "os_version"... { Args::StringToVersion(value, m_os_version_major, m_os_version_minor, m_os_version_update); if (m_os_version_major != UINT32_MAX) ++num_keys_decoded; } else if (name.equals("watchpoint_exceptions_received")) { m_watchpoints_trigger_after_instruction = llvm::StringSwitch(value) .Case("before", eLazyBoolNo) .Case("after", eLazyBoolYes) .Default(eLazyBoolCalculate); if (m_watchpoints_trigger_after_instruction != eLazyBoolCalculate) ++num_keys_decoded; } else if (name.equals("default_packet_timeout")) { uint32_t timeout_seconds; if (!value.getAsInteger(0, timeout_seconds)) { m_default_packet_timeout = seconds(timeout_seconds); SetPacketTimeout(m_default_packet_timeout); ++num_keys_decoded; } } } if (num_keys_decoded > 0) m_qHostInfo_is_valid = eLazyBoolYes; if (triple.empty()) { if (arch_name.empty()) { if (cpu != LLDB_INVALID_CPUTYPE) { m_host_arch.SetArchitecture(eArchTypeMachO, cpu, sub); if (pointer_byte_size) { assert(pointer_byte_size == m_host_arch.GetAddressByteSize()); } if (byte_order != eByteOrderInvalid) { assert(byte_order == m_host_arch.GetByteOrder()); } if (!vendor_name.empty()) m_host_arch.GetTriple().setVendorName( llvm::StringRef(vendor_name)); if (!os_name.empty()) m_host_arch.GetTriple().setOSName(llvm::StringRef(os_name)); } } else { std::string triple; triple += arch_name; if (!vendor_name.empty() || !os_name.empty()) { triple += '-'; if (vendor_name.empty()) triple += "unknown"; else triple += vendor_name; triple += '-'; if (os_name.empty()) triple += "unknown"; else triple += os_name; } m_host_arch.SetTriple(triple.c_str()); llvm::Triple &host_triple = m_host_arch.GetTriple(); if (host_triple.getVendor() == llvm::Triple::Apple && host_triple.getOS() == llvm::Triple::Darwin) { switch (m_host_arch.GetMachine()) { case llvm::Triple::aarch64: case llvm::Triple::arm: case llvm::Triple::thumb: host_triple.setOS(llvm::Triple::IOS); break; default: host_triple.setOS(llvm::Triple::MacOSX); break; } } if (pointer_byte_size) { assert(pointer_byte_size == m_host_arch.GetAddressByteSize()); } if (byte_order != eByteOrderInvalid) { assert(byte_order == m_host_arch.GetByteOrder()); } } } else { m_host_arch.SetTriple(triple.c_str()); if (pointer_byte_size) { assert(pointer_byte_size == m_host_arch.GetAddressByteSize()); } if (byte_order != eByteOrderInvalid) { assert(byte_order == m_host_arch.GetByteOrder()); } if (log) log->Printf("GDBRemoteCommunicationClient::%s parsed host " "architecture as %s, triple as %s from triple text %s", __FUNCTION__, m_host_arch.GetArchitectureName() ? m_host_arch.GetArchitectureName() : "", m_host_arch.GetTriple().getTriple().c_str(), triple.c_str()); } if (!distribution_id.empty()) m_host_arch.SetDistributionId(distribution_id.c_str()); } } } return m_qHostInfo_is_valid == eLazyBoolYes; } int GDBRemoteCommunicationClient::SendAttach( lldb::pid_t pid, StringExtractorGDBRemote &response) { if (pid != LLDB_INVALID_PROCESS_ID) { char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "vAttach;%" PRIx64, pid); UNUSED_IF_ASSERT_DISABLED(packet_len); assert(packet_len < (int)sizeof(packet)); if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsErrorResponse()) return response.GetError(); return 0; } } return -1; } int GDBRemoteCommunicationClient::SendStdinNotification(const char *data, size_t data_len) { StreamString packet; packet.PutCString("I"); packet.PutBytesAsRawHex8(data, data_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { return 0; } return response.GetError(); } const lldb_private::ArchSpec & GDBRemoteCommunicationClient::GetHostArchitecture() { if (m_qHostInfo_is_valid == eLazyBoolCalculate) GetHostInfo(); return m_host_arch; } seconds GDBRemoteCommunicationClient::GetHostDefaultPacketTimeout() { if (m_qHostInfo_is_valid == eLazyBoolCalculate) GetHostInfo(); return m_default_packet_timeout; } addr_t GDBRemoteCommunicationClient::AllocateMemory(size_t size, uint32_t permissions) { if (m_supports_alloc_dealloc_memory != eLazyBoolNo) { m_supports_alloc_dealloc_memory = eLazyBoolYes; char packet[64]; const int packet_len = ::snprintf( packet, sizeof(packet), "_M%" PRIx64 ",%s%s%s", (uint64_t)size, permissions & lldb::ePermissionsReadable ? "r" : "", permissions & lldb::ePermissionsWritable ? "w" : "", permissions & lldb::ePermissionsExecutable ? "x" : ""); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) m_supports_alloc_dealloc_memory = eLazyBoolNo; else if (!response.IsErrorResponse()) return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); } else { m_supports_alloc_dealloc_memory = eLazyBoolNo; } } return LLDB_INVALID_ADDRESS; } bool GDBRemoteCommunicationClient::DeallocateMemory(addr_t addr) { if (m_supports_alloc_dealloc_memory != eLazyBoolNo) { m_supports_alloc_dealloc_memory = eLazyBoolYes; char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "_m%" PRIx64, (uint64_t)addr); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) m_supports_alloc_dealloc_memory = eLazyBoolNo; else if (response.IsOKResponse()) return true; } else { m_supports_alloc_dealloc_memory = eLazyBoolNo; } } return false; } Status GDBRemoteCommunicationClient::Detach(bool keep_stopped) { Status error; if (keep_stopped) { if (m_supports_detach_stay_stopped == eLazyBoolCalculate) { char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "qSupportsDetachAndStayStopped:"); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success && response.IsOKResponse()) { m_supports_detach_stay_stopped = eLazyBoolYes; } else { m_supports_detach_stay_stopped = eLazyBoolNo; } } if (m_supports_detach_stay_stopped == eLazyBoolNo) { error.SetErrorString("Stays stopped not supported by this target."); return error; } else { StringExtractorGDBRemote response; PacketResult packet_result = SendPacketAndWaitForResponse("D1", response, false); if (packet_result != PacketResult::Success) error.SetErrorString("Sending extended disconnect packet failed."); } } else { StringExtractorGDBRemote response; PacketResult packet_result = SendPacketAndWaitForResponse("D", response, false); if (packet_result != PacketResult::Success) error.SetErrorString("Sending disconnect packet failed."); } return error; } Status GDBRemoteCommunicationClient::GetMemoryRegionInfo( lldb::addr_t addr, lldb_private::MemoryRegionInfo ®ion_info) { Status error; region_info.Clear(); if (m_supports_memory_region_info != eLazyBoolNo) { m_supports_memory_region_info = eLazyBoolYes; char packet[64]; const int packet_len = ::snprintf( packet, sizeof(packet), "qMemoryRegionInfo:%" PRIx64, (uint64_t)addr); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { llvm::StringRef name; llvm::StringRef value; addr_t addr_value = LLDB_INVALID_ADDRESS; bool success = true; bool saw_permissions = false; while (success && response.GetNameColonValue(name, value)) { if (name.equals("start")) { if (!value.getAsInteger(16, addr_value)) region_info.GetRange().SetRangeBase(addr_value); } else if (name.equals("size")) { if (!value.getAsInteger(16, addr_value)) region_info.GetRange().SetByteSize(addr_value); } else if (name.equals("permissions") && region_info.GetRange().IsValid()) { saw_permissions = true; if (region_info.GetRange().Contains(addr)) { if (value.find('r') != llvm::StringRef::npos) region_info.SetReadable(MemoryRegionInfo::eYes); else region_info.SetReadable(MemoryRegionInfo::eNo); if (value.find('w') != llvm::StringRef::npos) region_info.SetWritable(MemoryRegionInfo::eYes); else region_info.SetWritable(MemoryRegionInfo::eNo); if (value.find('x') != llvm::StringRef::npos) region_info.SetExecutable(MemoryRegionInfo::eYes); else region_info.SetExecutable(MemoryRegionInfo::eNo); region_info.SetMapped(MemoryRegionInfo::eYes); } else { // The reported region does not contain this address -- we're // looking at an unmapped page region_info.SetReadable(MemoryRegionInfo::eNo); region_info.SetWritable(MemoryRegionInfo::eNo); region_info.SetExecutable(MemoryRegionInfo::eNo); region_info.SetMapped(MemoryRegionInfo::eNo); } } else if (name.equals("name")) { StringExtractorGDBRemote name_extractor(value); std::string name; name_extractor.GetHexByteString(name); region_info.SetName(name.c_str()); } else if (name.equals("error")) { StringExtractorGDBRemote error_extractor(value); std::string error_string; // Now convert the HEX bytes into a string value error_extractor.GetHexByteString(error_string); error.SetErrorString(error_string.c_str()); } } if (region_info.GetRange().IsValid()) { // We got a valid address range back but no permissions -- which means // this is an unmapped page if (!saw_permissions) { region_info.SetReadable(MemoryRegionInfo::eNo); region_info.SetWritable(MemoryRegionInfo::eNo); region_info.SetExecutable(MemoryRegionInfo::eNo); region_info.SetMapped(MemoryRegionInfo::eNo); } } else { // We got an invalid address range back error.SetErrorString("Server returned invalid range"); } } else { m_supports_memory_region_info = eLazyBoolNo; } } if (m_supports_memory_region_info == eLazyBoolNo) { error.SetErrorString("qMemoryRegionInfo is not supported"); } if (error.Fail()) region_info.Clear(); return error; } Status GDBRemoteCommunicationClient::GetWatchpointSupportInfo(uint32_t &num) { Status error; if (m_supports_watchpoint_support_info == eLazyBoolYes) { num = m_num_supported_hardware_watchpoints; return error; } // Set num to 0 first. num = 0; if (m_supports_watchpoint_support_info != eLazyBoolNo) { char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "qWatchpointSupportInfo:"); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { m_supports_watchpoint_support_info = eLazyBoolYes; llvm::StringRef name; llvm::StringRef value; while (response.GetNameColonValue(name, value)) { if (name.equals("num")) { value.getAsInteger(0, m_num_supported_hardware_watchpoints); num = m_num_supported_hardware_watchpoints; } } } else { m_supports_watchpoint_support_info = eLazyBoolNo; } } if (m_supports_watchpoint_support_info == eLazyBoolNo) { error.SetErrorString("qWatchpointSupportInfo is not supported"); } return error; } lldb_private::Status GDBRemoteCommunicationClient::GetWatchpointSupportInfo( uint32_t &num, bool &after, const ArchSpec &arch) { Status error(GetWatchpointSupportInfo(num)); if (error.Success()) error = GetWatchpointsTriggerAfterInstruction(after, arch); return error; } lldb_private::Status GDBRemoteCommunicationClient::GetWatchpointsTriggerAfterInstruction( bool &after, const ArchSpec &arch) { Status error; llvm::Triple::ArchType atype = arch.GetMachine(); // we assume watchpoints will happen after running the relevant opcode // and we only want to override this behavior if we have explicitly // received a qHostInfo telling us otherwise if (m_qHostInfo_is_valid != eLazyBoolYes) { // On targets like MIPS, watchpoint exceptions are always generated // before the instruction is executed. The connected target may not // support qHostInfo or qWatchpointSupportInfo packets. if (atype == llvm::Triple::mips || atype == llvm::Triple::mipsel || atype == llvm::Triple::mips64 || atype == llvm::Triple::mips64el) after = false; else after = true; } else { // For MIPS, set m_watchpoints_trigger_after_instruction to eLazyBoolNo // if it is not calculated before. if (m_watchpoints_trigger_after_instruction == eLazyBoolCalculate && (atype == llvm::Triple::mips || atype == llvm::Triple::mipsel || atype == llvm::Triple::mips64 || atype == llvm::Triple::mips64el)) m_watchpoints_trigger_after_instruction = eLazyBoolNo; after = (m_watchpoints_trigger_after_instruction != eLazyBoolNo); } return error; } int GDBRemoteCommunicationClient::SetSTDIN(const FileSpec &file_spec) { if (file_spec) { std::string path{file_spec.GetPath(false)}; StreamString packet; packet.PutCString("QSetSTDIN:"); packet.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } int GDBRemoteCommunicationClient::SetSTDOUT(const FileSpec &file_spec) { if (file_spec) { std::string path{file_spec.GetPath(false)}; StreamString packet; packet.PutCString("QSetSTDOUT:"); packet.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } int GDBRemoteCommunicationClient::SetSTDERR(const FileSpec &file_spec) { if (file_spec) { std::string path{file_spec.GetPath(false)}; StreamString packet; packet.PutCString("QSetSTDERR:"); packet.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } bool GDBRemoteCommunicationClient::GetWorkingDir(FileSpec &working_dir) { StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qGetWorkingDir", response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) return false; if (response.IsErrorResponse()) return false; std::string cwd; response.GetHexByteString(cwd); working_dir.SetFile(cwd, false, GetHostArchitecture().GetTriple()); return !cwd.empty(); } return false; } int GDBRemoteCommunicationClient::SetWorkingDir(const FileSpec &working_dir) { if (working_dir) { std::string path{working_dir.GetPath(false)}; StreamString packet; packet.PutCString("QSetWorkingDir:"); packet.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } int GDBRemoteCommunicationClient::SetDisableASLR(bool enable) { char packet[32]; const int packet_len = ::snprintf(packet, sizeof(packet), "QSetDisableASLR:%i", enable ? 1 : 0); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } return -1; } int GDBRemoteCommunicationClient::SetDetachOnError(bool enable) { char packet[32]; const int packet_len = ::snprintf(packet, sizeof(packet), "QSetDetachOnError:%i", enable ? 1 : 0); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } return -1; } bool GDBRemoteCommunicationClient::DecodeProcessInfoResponse( StringExtractorGDBRemote &response, ProcessInstanceInfo &process_info) { if (response.IsNormalResponse()) { llvm::StringRef name; llvm::StringRef value; StringExtractor extractor; uint32_t cpu = LLDB_INVALID_CPUTYPE; uint32_t sub = 0; std::string vendor; std::string os_type; while (response.GetNameColonValue(name, value)) { if (name.equals("pid")) { lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; value.getAsInteger(0, pid); process_info.SetProcessID(pid); } else if (name.equals("ppid")) { lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; value.getAsInteger(0, pid); process_info.SetParentProcessID(pid); } else if (name.equals("uid")) { uint32_t uid = UINT32_MAX; value.getAsInteger(0, uid); process_info.SetUserID(uid); } else if (name.equals("euid")) { uint32_t uid = UINT32_MAX; value.getAsInteger(0, uid); process_info.SetEffectiveGroupID(uid); } else if (name.equals("gid")) { uint32_t gid = UINT32_MAX; value.getAsInteger(0, gid); process_info.SetGroupID(gid); } else if (name.equals("egid")) { uint32_t gid = UINT32_MAX; value.getAsInteger(0, gid); process_info.SetEffectiveGroupID(gid); } else if (name.equals("triple")) { StringExtractor extractor(value); std::string triple; extractor.GetHexByteString(triple); process_info.GetArchitecture().SetTriple(triple.c_str()); } else if (name.equals("name")) { StringExtractor extractor(value); // The process name from ASCII hex bytes since we can't // control the characters in a process name std::string name; extractor.GetHexByteString(name); process_info.GetExecutableFile().SetFile(name, false); } else if (name.equals("cputype")) { value.getAsInteger(0, cpu); } else if (name.equals("cpusubtype")) { value.getAsInteger(0, sub); } else if (name.equals("vendor")) { vendor = value; } else if (name.equals("ostype")) { os_type = value; } } if (cpu != LLDB_INVALID_CPUTYPE && !vendor.empty() && !os_type.empty()) { if (vendor == "apple") { process_info.GetArchitecture().SetArchitecture(eArchTypeMachO, cpu, sub); process_info.GetArchitecture().GetTriple().setVendorName( llvm::StringRef(vendor)); process_info.GetArchitecture().GetTriple().setOSName( llvm::StringRef(os_type)); } } if (process_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) return true; } return false; } bool GDBRemoteCommunicationClient::GetProcessInfo( lldb::pid_t pid, ProcessInstanceInfo &process_info) { process_info.Clear(); if (m_supports_qProcessInfoPID) { char packet[32]; const int packet_len = ::snprintf(packet, sizeof(packet), "qProcessInfoPID:%" PRIu64, pid); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { return DecodeProcessInfoResponse(response, process_info); } else { m_supports_qProcessInfoPID = false; return false; } } return false; } bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) { Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); if (allow_lazy) { if (m_qProcessInfo_is_valid == eLazyBoolYes) return true; if (m_qProcessInfo_is_valid == eLazyBoolNo) return false; } GetHostInfo(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qProcessInfo", response, false) == PacketResult::Success) { if (response.IsNormalResponse()) { llvm::StringRef name; llvm::StringRef value; uint32_t cpu = LLDB_INVALID_CPUTYPE; uint32_t sub = 0; std::string arch_name; std::string os_name; std::string vendor_name; std::string triple; std::string elf_abi; uint32_t pointer_byte_size = 0; StringExtractor extractor; ByteOrder byte_order = eByteOrderInvalid; uint32_t num_keys_decoded = 0; lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; while (response.GetNameColonValue(name, value)) { if (name.equals("cputype")) { if (!value.getAsInteger(16, cpu)) ++num_keys_decoded; } else if (name.equals("cpusubtype")) { if (!value.getAsInteger(16, sub)) ++num_keys_decoded; } else if (name.equals("triple")) { StringExtractor extractor(value); extractor.GetHexByteString(triple); ++num_keys_decoded; } else if (name.equals("ostype")) { os_name = value; ++num_keys_decoded; } else if (name.equals("vendor")) { vendor_name = value; ++num_keys_decoded; } else if (name.equals("endian")) { byte_order = llvm::StringSwitch(value) .Case("little", eByteOrderLittle) .Case("big", eByteOrderBig) .Case("pdp", eByteOrderPDP) .Default(eByteOrderInvalid); if (byte_order != eByteOrderInvalid) ++num_keys_decoded; } else if (name.equals("ptrsize")) { if (!value.getAsInteger(16, pointer_byte_size)) ++num_keys_decoded; } else if (name.equals("pid")) { if (!value.getAsInteger(16, pid)) ++num_keys_decoded; } else if (name.equals("elf_abi")) { elf_abi = value; ++num_keys_decoded; } } if (num_keys_decoded > 0) m_qProcessInfo_is_valid = eLazyBoolYes; if (pid != LLDB_INVALID_PROCESS_ID) { m_curr_pid_is_valid = eLazyBoolYes; m_curr_pid = pid; } // Set the ArchSpec from the triple if we have it. if (!triple.empty()) { m_process_arch.SetTriple(triple.c_str()); m_process_arch.SetFlags(elf_abi); if (pointer_byte_size) { assert(pointer_byte_size == m_process_arch.GetAddressByteSize()); } } else if (cpu != LLDB_INVALID_CPUTYPE && !os_name.empty() && !vendor_name.empty()) { llvm::Triple triple(llvm::Twine("-") + vendor_name + "-" + os_name); assert(triple.getObjectFormat() != llvm::Triple::UnknownObjectFormat); assert(triple.getObjectFormat() != llvm::Triple::Wasm); switch (triple.getObjectFormat()) { case llvm::Triple::MachO: m_process_arch.SetArchitecture(eArchTypeMachO, cpu, sub); break; case llvm::Triple::ELF: m_process_arch.SetArchitecture(eArchTypeELF, cpu, sub); break; case llvm::Triple::COFF: m_process_arch.SetArchitecture(eArchTypeCOFF, cpu, sub); break; case llvm::Triple::Wasm: if (log) log->Printf("error: not supported target architecture"); return false; case llvm::Triple::UnknownObjectFormat: if (log) log->Printf("error: failed to determine target architecture"); return false; } if (pointer_byte_size) { assert(pointer_byte_size == m_process_arch.GetAddressByteSize()); } if (byte_order != eByteOrderInvalid) { assert(byte_order == m_process_arch.GetByteOrder()); } m_process_arch.GetTriple().setVendorName(llvm::StringRef(vendor_name)); m_process_arch.GetTriple().setOSName(llvm::StringRef(os_name)); m_host_arch.GetTriple().setVendorName(llvm::StringRef(vendor_name)); m_host_arch.GetTriple().setOSName(llvm::StringRef(os_name)); } return true; } } else { m_qProcessInfo_is_valid = eLazyBoolNo; } return false; } uint32_t GDBRemoteCommunicationClient::FindProcesses( const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) { process_infos.Clear(); if (m_supports_qfProcessInfo) { StreamString packet; packet.PutCString("qfProcessInfo"); if (!match_info.MatchAllProcesses()) { packet.PutChar(':'); const char *name = match_info.GetProcessInfo().GetName(); bool has_name_match = false; if (name && name[0]) { has_name_match = true; NameMatch name_match_type = match_info.GetNameMatchType(); switch (name_match_type) { case NameMatch::Ignore: has_name_match = false; break; case NameMatch::Equals: packet.PutCString("name_match:equals;"); break; case NameMatch::Contains: packet.PutCString("name_match:contains;"); break; case NameMatch::StartsWith: packet.PutCString("name_match:starts_with;"); break; case NameMatch::EndsWith: packet.PutCString("name_match:ends_with;"); break; case NameMatch::RegularExpression: packet.PutCString("name_match:regex;"); break; } if (has_name_match) { packet.PutCString("name:"); packet.PutBytesAsRawHex8(name, ::strlen(name)); packet.PutChar(';'); } } if (match_info.GetProcessInfo().ProcessIDIsValid()) packet.Printf("pid:%" PRIu64 ";", match_info.GetProcessInfo().GetProcessID()); if (match_info.GetProcessInfo().ParentProcessIDIsValid()) packet.Printf("parent_pid:%" PRIu64 ";", match_info.GetProcessInfo().GetParentProcessID()); if (match_info.GetProcessInfo().UserIDIsValid()) packet.Printf("uid:%u;", match_info.GetProcessInfo().GetUserID()); if (match_info.GetProcessInfo().GroupIDIsValid()) packet.Printf("gid:%u;", match_info.GetProcessInfo().GetGroupID()); if (match_info.GetProcessInfo().EffectiveUserIDIsValid()) packet.Printf("euid:%u;", match_info.GetProcessInfo().GetEffectiveUserID()); if (match_info.GetProcessInfo().EffectiveGroupIDIsValid()) packet.Printf("egid:%u;", match_info.GetProcessInfo().GetEffectiveGroupID()); if (match_info.GetProcessInfo().EffectiveGroupIDIsValid()) packet.Printf("all_users:%u;", match_info.GetMatchAllUsers() ? 1 : 0); if (match_info.GetProcessInfo().GetArchitecture().IsValid()) { const ArchSpec &match_arch = match_info.GetProcessInfo().GetArchitecture(); const llvm::Triple &triple = match_arch.GetTriple(); packet.PutCString("triple:"); packet.PutCString(triple.getTriple()); packet.PutChar(';'); } } StringExtractorGDBRemote response; // Increase timeout as the first qfProcessInfo packet takes a long time // on Android. The value of 1min was arrived at empirically. ScopedTimeout timeout(*this, minutes(1)); if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { do { ProcessInstanceInfo process_info; if (!DecodeProcessInfoResponse(response, process_info)) break; process_infos.Append(process_info); response.GetStringRef().clear(); response.SetFilePos(0); } while (SendPacketAndWaitForResponse("qsProcessInfo", response, false) == PacketResult::Success); } else { m_supports_qfProcessInfo = false; return 0; } } return process_infos.GetSize(); } bool GDBRemoteCommunicationClient::GetUserName(uint32_t uid, std::string &name) { if (m_supports_qUserName) { char packet[32]; const int packet_len = ::snprintf(packet, sizeof(packet), "qUserName:%i", uid); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsNormalResponse()) { // Make sure we parsed the right number of characters. The response is // the hex encoded user name and should make up the entire packet. // If there are any non-hex ASCII bytes, the length won't match below.. if (response.GetHexByteString(name) * 2 == response.GetStringRef().size()) return true; } } else { m_supports_qUserName = false; return false; } } return false; } bool GDBRemoteCommunicationClient::GetGroupName(uint32_t gid, std::string &name) { if (m_supports_qGroupName) { char packet[32]; const int packet_len = ::snprintf(packet, sizeof(packet), "qGroupName:%i", gid); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsNormalResponse()) { // Make sure we parsed the right number of characters. The response is // the hex encoded group name and should make up the entire packet. // If there are any non-hex ASCII bytes, the length won't match below.. if (response.GetHexByteString(name) * 2 == response.GetStringRef().size()) return true; } } else { m_supports_qGroupName = false; return false; } } return false; } bool GDBRemoteCommunicationClient::SetNonStopMode(const bool enable) { // Form non-stop packet request char packet[32]; const int packet_len = ::snprintf(packet, sizeof(packet), "QNonStop:%1d", (int)enable); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; // Send to target if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) if (response.IsOKResponse()) return true; // Failed or not supported return false; } static void MakeSpeedTestPacket(StreamString &packet, uint32_t send_size, uint32_t recv_size) { packet.Clear(); packet.Printf("qSpeedTest:response_size:%i;data:", recv_size); uint32_t bytes_left = send_size; while (bytes_left > 0) { if (bytes_left >= 26) { packet.PutCString("abcdefghijklmnopqrstuvwxyz"); bytes_left -= 26; } else { packet.Printf("%*.*s;", bytes_left, bytes_left, "abcdefghijklmnopqrstuvwxyz"); bytes_left = 0; } } } duration calculate_standard_deviation(const std::vector> &v) { using Dur = duration; Dur sum = std::accumulate(std::begin(v), std::end(v), Dur()); Dur mean = sum / v.size(); float accum = 0; for (auto d : v) { float delta = (d - mean).count(); accum += delta * delta; }; return Dur(sqrtf(accum / (v.size() - 1))); } void GDBRemoteCommunicationClient::TestPacketSpeed(const uint32_t num_packets, uint32_t max_send, uint32_t max_recv, uint64_t recv_amount, bool json, Stream &strm) { uint32_t i; if (SendSpeedTestPacket(0, 0)) { StreamString packet; if (json) strm.Printf("{ \"packet_speeds\" : {\n \"num_packets\" : %u,\n " "\"results\" : [", num_packets); else strm.Printf("Testing sending %u packets of various sizes:\n", num_packets); strm.Flush(); uint32_t result_idx = 0; uint32_t send_size; std::vector> packet_times; for (send_size = 0; send_size <= max_send; send_size ? send_size *= 2 : send_size = 4) { for (uint32_t recv_size = 0; recv_size <= max_recv; recv_size ? recv_size *= 2 : recv_size = 4) { MakeSpeedTestPacket(packet, send_size, recv_size); packet_times.clear(); // Test how long it takes to send 'num_packets' packets const auto start_time = steady_clock::now(); for (i = 0; i < num_packets; ++i) { const auto packet_start_time = steady_clock::now(); StringExtractorGDBRemote response; SendPacketAndWaitForResponse(packet.GetString(), response, false); const auto packet_end_time = steady_clock::now(); packet_times.push_back(packet_end_time - packet_start_time); } const auto end_time = steady_clock::now(); const auto total_time = end_time - start_time; float packets_per_second = ((float)num_packets) / duration(total_time).count(); auto average_per_packet = total_time / num_packets; const duration standard_deviation = calculate_standard_deviation(packet_times); if (json) { strm.Format("{0}\n {{\"send_size\" : {1,6}, \"recv_size\" : " "{2,6}, \"total_time_nsec\" : {3,12:ns-}, " "\"standard_deviation_nsec\" : {4,9:ns-f0}}", result_idx > 0 ? "," : "", send_size, recv_size, total_time, standard_deviation); ++result_idx; } else { strm.Format("qSpeedTest(send={0,7}, recv={1,7}) in {2:s+f9} for " "{3,9:f2} packets/s ({4,10:ms+f6} per packet) with " "standard deviation of {5,10:ms+f6}\n", send_size, recv_size, duration(total_time), packets_per_second, duration(average_per_packet), standard_deviation); } strm.Flush(); } } const float k_recv_amount_mb = (float)recv_amount / (1024.0f * 1024.0f); if (json) strm.Printf("\n ]\n },\n \"download_speed\" : {\n \"byte_size\" " ": %" PRIu64 ",\n \"results\" : [", recv_amount); else strm.Printf("Testing receiving %2.1fMB of data using varying receive " "packet sizes:\n", k_recv_amount_mb); strm.Flush(); send_size = 0; result_idx = 0; for (uint32_t recv_size = 32; recv_size <= max_recv; recv_size *= 2) { MakeSpeedTestPacket(packet, send_size, recv_size); // If we have a receive size, test how long it takes to receive 4MB of // data if (recv_size > 0) { const auto start_time = steady_clock::now(); uint32_t bytes_read = 0; uint32_t packet_count = 0; while (bytes_read < recv_amount) { StringExtractorGDBRemote response; SendPacketAndWaitForResponse(packet.GetString(), response, false); bytes_read += recv_size; ++packet_count; } const auto end_time = steady_clock::now(); const auto total_time = end_time - start_time; float mb_second = ((float)recv_amount) / duration(total_time).count() / (1024.0 * 1024.0); float packets_per_second = ((float)packet_count) / duration(total_time).count(); const auto average_per_packet = total_time / packet_count; if (json) { strm.Format("{0}\n {{\"send_size\" : {1,6}, \"recv_size\" : " "{2,6}, \"total_time_nsec\" : {3,12:ns-}}", result_idx > 0 ? "," : "", send_size, recv_size, total_time); ++result_idx; } else { strm.Format("qSpeedTest(send={0,7}, recv={1,7}) {2,6} packets needed " "to receive {3:f1}MB in {4:s+f9} for {5} MB/sec for " "{6,9:f2} packets/sec ({7,10:ms+f6} per packet)\n", send_size, recv_size, packet_count, k_recv_amount_mb, duration(total_time), mb_second, packets_per_second, duration(average_per_packet)); } strm.Flush(); } } if (json) strm.Printf("\n ]\n }\n}\n"); else strm.EOL(); } } bool GDBRemoteCommunicationClient::SendSpeedTestPacket(uint32_t send_size, uint32_t recv_size) { StreamString packet; packet.Printf("qSpeedTest:response_size:%i;data:", recv_size); uint32_t bytes_left = send_size; while (bytes_left > 0) { if (bytes_left >= 26) { packet.PutCString("abcdefghijklmnopqrstuvwxyz"); bytes_left -= 26; } else { packet.Printf("%*.*s;", bytes_left, bytes_left, "abcdefghijklmnopqrstuvwxyz"); bytes_left = 0; } } StringExtractorGDBRemote response; return SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success; } bool GDBRemoteCommunicationClient::LaunchGDBServer( const char *remote_accept_hostname, lldb::pid_t &pid, uint16_t &port, std::string &socket_name) { pid = LLDB_INVALID_PROCESS_ID; port = 0; socket_name.clear(); StringExtractorGDBRemote response; StreamString stream; stream.PutCString("qLaunchGDBServer;"); std::string hostname; if (remote_accept_hostname && remote_accept_hostname[0]) hostname = remote_accept_hostname; else { if (HostInfo::GetHostname(hostname)) { // Make the GDB server we launch only accept connections from this host stream.Printf("host:%s;", hostname.c_str()); } else { // Make the GDB server we launch accept connections from any host since we // can't figure out the hostname stream.Printf("host:*;"); } } // give the process a few seconds to startup ScopedTimeout timeout(*this, seconds(10)); if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { llvm::StringRef name; llvm::StringRef value; while (response.GetNameColonValue(name, value)) { if (name.equals("port")) value.getAsInteger(0, port); else if (name.equals("pid")) value.getAsInteger(0, pid); else if (name.compare("socket_name") == 0) { StringExtractor extractor(value); extractor.GetHexByteString(socket_name); } } return true; } return false; } size_t GDBRemoteCommunicationClient::QueryGDBServer( std::vector> &connection_urls) { connection_urls.clear(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qQueryGDBServer", response, false) != PacketResult::Success) return 0; StructuredData::ObjectSP data = StructuredData::ParseJSON(response.GetStringRef()); if (!data) return 0; StructuredData::Array *array = data->GetAsArray(); if (!array) return 0; for (size_t i = 0, count = array->GetSize(); i < count; ++i) { StructuredData::Dictionary *element = nullptr; if (!array->GetItemAtIndexAsDictionary(i, element)) continue; uint16_t port = 0; if (StructuredData::ObjectSP port_osp = element->GetValueForKey(llvm::StringRef("port"))) port = port_osp->GetIntegerValue(0); std::string socket_name; if (StructuredData::ObjectSP socket_name_osp = element->GetValueForKey(llvm::StringRef("socket_name"))) socket_name = socket_name_osp->GetStringValue(); if (port != 0 || !socket_name.empty()) connection_urls.emplace_back(port, socket_name); } return connection_urls.size(); } bool GDBRemoteCommunicationClient::KillSpawnedProcess(lldb::pid_t pid) { StreamString stream; stream.Printf("qKillSpawnedProcess:%" PRId64, pid); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return true; } return false; } bool GDBRemoteCommunicationClient::SetCurrentThread(uint64_t tid) { if (m_curr_tid == tid) return true; char packet[32]; int packet_len; if (tid == UINT64_MAX) packet_len = ::snprintf(packet, sizeof(packet), "Hg-1"); else packet_len = ::snprintf(packet, sizeof(packet), "Hg%" PRIx64, tid); assert(packet_len + 1 < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_curr_tid = tid; return true; } /* * Connected bare-iron target (like YAMON gdb-stub) may not have support for * Hg packet. * The reply from '?' packet could be as simple as 'S05'. There is no packet * which can * give us pid and/or tid. Assume pid=tid=1 in such cases. */ if (response.IsUnsupportedResponse() && IsConnected()) { m_curr_tid = 1; return true; } } return false; } bool GDBRemoteCommunicationClient::SetCurrentThreadForRun(uint64_t tid) { if (m_curr_tid_run == tid) return true; char packet[32]; int packet_len; if (tid == UINT64_MAX) packet_len = ::snprintf(packet, sizeof(packet), "Hc-1"); else packet_len = ::snprintf(packet, sizeof(packet), "Hc%" PRIx64, tid); assert(packet_len + 1 < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_curr_tid_run = tid; return true; } /* * Connected bare-iron target (like YAMON gdb-stub) may not have support for * Hc packet. * The reply from '?' packet could be as simple as 'S05'. There is no packet * which can * give us pid and/or tid. Assume pid=tid=1 in such cases. */ if (response.IsUnsupportedResponse() && IsConnected()) { m_curr_tid_run = 1; return true; } } return false; } bool GDBRemoteCommunicationClient::GetStopReply( StringExtractorGDBRemote &response) { if (SendPacketAndWaitForResponse("?", response, false) == PacketResult::Success) return response.IsNormalResponse(); return false; } bool GDBRemoteCommunicationClient::GetThreadStopInfo( lldb::tid_t tid, StringExtractorGDBRemote &response) { if (m_supports_qThreadStopInfo) { char packet[256]; int packet_len = ::snprintf(packet, sizeof(packet), "qThreadStopInfo%" PRIx64, tid); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) m_supports_qThreadStopInfo = false; else if (response.IsNormalResponse()) return true; else return false; } else { m_supports_qThreadStopInfo = false; } } return false; } uint8_t GDBRemoteCommunicationClient::SendGDBStoppointTypePacket( GDBStoppointType type, bool insert, addr_t addr, uint32_t length) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf("GDBRemoteCommunicationClient::%s() %s at addr = 0x%" PRIx64, __FUNCTION__, insert ? "add" : "remove", addr); // Check if the stub is known not to support this breakpoint type if (!SupportsGDBStoppointPacket(type)) return UINT8_MAX; // Construct the breakpoint packet char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "%c%i,%" PRIx64 ",%x", insert ? 'Z' : 'z', type, addr, length); // Check we haven't overwritten the end of the packet buffer assert(packet_len + 1 < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; // Make sure the response is either "OK", "EXX" where XX are two hex digits, // or "" (unsupported) response.SetResponseValidatorToOKErrorNotSupported(); // Try to send the breakpoint packet, and check that it was correctly sent if (SendPacketAndWaitForResponse(packet, response, true) == PacketResult::Success) { // Receive and OK packet when the breakpoint successfully placed if (response.IsOKResponse()) return 0; // Status while setting breakpoint, send back specific error if (response.IsErrorResponse()) return response.GetError(); // Empty packet informs us that breakpoint is not supported if (response.IsUnsupportedResponse()) { // Disable this breakpoint type since it is unsupported switch (type) { case eBreakpointSoftware: m_supports_z0 = false; break; case eBreakpointHardware: m_supports_z1 = false; break; case eWatchpointWrite: m_supports_z2 = false; break; case eWatchpointRead: m_supports_z3 = false; break; case eWatchpointReadWrite: m_supports_z4 = false; break; case eStoppointInvalid: return UINT8_MAX; } } } // Signal generic failure return UINT8_MAX; } size_t GDBRemoteCommunicationClient::GetCurrentThreadIDs( std::vector &thread_ids, bool &sequence_mutex_unavailable) { thread_ids.clear(); Lock lock(*this, false); if (lock) { sequence_mutex_unavailable = false; StringExtractorGDBRemote response; PacketResult packet_result; for (packet_result = SendPacketAndWaitForResponseNoLock("qfThreadInfo", response); packet_result == PacketResult::Success && response.IsNormalResponse(); packet_result = SendPacketAndWaitForResponseNoLock("qsThreadInfo", response)) { char ch = response.GetChar(); if (ch == 'l') break; if (ch == 'm') { do { tid_t tid = response.GetHexMaxU64(false, LLDB_INVALID_THREAD_ID); if (tid != LLDB_INVALID_THREAD_ID) { thread_ids.push_back(tid); } ch = response.GetChar(); // Skip the command separator } while (ch == ','); // Make sure we got a comma separator } } /* * Connected bare-iron target (like YAMON gdb-stub) may not have support for * qProcessInfo, qC and qfThreadInfo packets. The reply from '?' packet * could * be as simple as 'S05'. There is no packet which can give us pid and/or * tid. * Assume pid=tid=1 in such cases. */ if (response.IsUnsupportedResponse() && thread_ids.size() == 0 && IsConnected()) { thread_ids.push_back(1); } } else { #if !defined(LLDB_CONFIGURATION_DEBUG) Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); if (log) log->Printf("error: failed to get packet sequence mutex, not sending " "packet 'qfThreadInfo'"); #endif sequence_mutex_unavailable = true; } return thread_ids.size(); } lldb::addr_t GDBRemoteCommunicationClient::GetShlibInfoAddr() { StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qShlibInfoAddr", response, false) != PacketResult::Success || !response.IsNormalResponse()) return LLDB_INVALID_ADDRESS; return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); } lldb_private::Status GDBRemoteCommunicationClient::RunShellCommand( const char *command, // Shouldn't be NULL const FileSpec & working_dir, // Pass empty FileSpec to use the current working directory int *status_ptr, // Pass NULL if you don't want the process exit status int *signo_ptr, // Pass NULL if you don't want the signal that caused the // process to exit std::string *command_output, // Pass NULL if you don't want the command output uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish { lldb_private::StreamString stream; stream.PutCString("qPlatform_shell:"); stream.PutBytesAsRawHex8(command, strlen(command)); stream.PutChar(','); stream.PutHex32(timeout_sec); if (working_dir) { std::string path{working_dir.GetPath(false)}; stream.PutChar(','); stream.PutCStringAsRawHex8(path.c_str()); } StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() != 'F') return Status("malformed reply"); if (response.GetChar() != ',') return Status("malformed reply"); uint32_t exitcode = response.GetHexMaxU32(false, UINT32_MAX); if (exitcode == UINT32_MAX) return Status("unable to run remote process"); else if (status_ptr) *status_ptr = exitcode; if (response.GetChar() != ',') return Status("malformed reply"); uint32_t signo = response.GetHexMaxU32(false, UINT32_MAX); if (signo_ptr) *signo_ptr = signo; if (response.GetChar() != ',') return Status("malformed reply"); std::string output; response.GetEscapedBinaryData(output); if (command_output) command_output->assign(output); return Status(); } return Status("unable to send packet"); } Status GDBRemoteCommunicationClient::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions) { std::string path{file_spec.GetPath(false)}; lldb_private::StreamString stream; stream.PutCString("qPlatform_mkdir:"); stream.PutHex32(file_permissions); stream.PutChar(','); stream.PutCStringAsRawHex8(path.c_str()); llvm::StringRef packet = stream.GetString(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) != PacketResult::Success) return Status("failed to send '%s' packet", packet.str().c_str()); if (response.GetChar() != 'F') return Status("invalid response to '%s' packet", packet.str().c_str()); return Status(response.GetU32(UINT32_MAX), eErrorTypePOSIX); } Status GDBRemoteCommunicationClient::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions) { std::string path{file_spec.GetPath(false)}; lldb_private::StreamString stream; stream.PutCString("qPlatform_chmod:"); stream.PutHex32(file_permissions); stream.PutChar(','); stream.PutCStringAsRawHex8(path.c_str()); llvm::StringRef packet = stream.GetString(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) != PacketResult::Success) return Status("failed to send '%s' packet", stream.GetData()); if (response.GetChar() != 'F') return Status("invalid response to '%s' packet", stream.GetData()); return Status(response.GetU32(UINT32_MAX), eErrorTypePOSIX); } static uint64_t ParseHostIOPacketResponse(StringExtractorGDBRemote &response, uint64_t fail_result, Status &error) { response.SetFilePos(0); if (response.GetChar() != 'F') return fail_result; int32_t result = response.GetS32(-2); if (result == -2) return fail_result; if (response.GetChar() == ',') { int result_errno = response.GetS32(-2); if (result_errno != -2) error.SetError(result_errno, eErrorTypePOSIX); else error.SetError(-1, eErrorTypeGeneric); } else error.Clear(); return result; } lldb::user_id_t GDBRemoteCommunicationClient::OpenFile(const lldb_private::FileSpec &file_spec, uint32_t flags, mode_t mode, Status &error) { std::string path(file_spec.GetPath(false)); lldb_private::StreamString stream; stream.PutCString("vFile:open:"); if (path.empty()) return UINT64_MAX; stream.PutCStringAsRawHex8(path.c_str()); stream.PutChar(','); stream.PutHex32(flags); stream.PutChar(','); stream.PutHex32(mode); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { return ParseHostIOPacketResponse(response, UINT64_MAX, error); } return UINT64_MAX; } bool GDBRemoteCommunicationClient::CloseFile(lldb::user_id_t fd, Status &error) { lldb_private::StreamString stream; stream.Printf("vFile:close:%i", (int)fd); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { return ParseHostIOPacketResponse(response, -1, error) == 0; } return false; } // Extension of host I/O packets to get the file size. lldb::user_id_t GDBRemoteCommunicationClient::GetFileSize( const lldb_private::FileSpec &file_spec) { std::string path(file_spec.GetPath(false)); lldb_private::StreamString stream; stream.PutCString("vFile:size:"); stream.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() != 'F') return UINT64_MAX; uint32_t retcode = response.GetHexMaxU64(false, UINT64_MAX); return retcode; } return UINT64_MAX; } Status GDBRemoteCommunicationClient::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions) { std::string path{file_spec.GetPath(false)}; Status error; lldb_private::StreamString stream; stream.PutCString("vFile:mode:"); stream.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() != 'F') { error.SetErrorStringWithFormat("invalid response to '%s' packet", stream.GetData()); } else { const uint32_t mode = response.GetS32(-1); if (static_cast(mode) == -1) { if (response.GetChar() == ',') { int response_errno = response.GetS32(-1); if (response_errno > 0) error.SetError(response_errno, lldb::eErrorTypePOSIX); else error.SetErrorToGenericError(); } else error.SetErrorToGenericError(); } else { file_permissions = mode & (S_IRWXU | S_IRWXG | S_IRWXO); } } } else { error.SetErrorStringWithFormat("failed to send '%s' packet", stream.GetData()); } return error; } uint64_t GDBRemoteCommunicationClient::ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst, uint64_t dst_len, Status &error) { lldb_private::StreamString stream; stream.Printf("vFile:pread:%i,%" PRId64 ",%" PRId64, (int)fd, dst_len, offset); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() != 'F') return 0; uint32_t retcode = response.GetHexMaxU32(false, UINT32_MAX); if (retcode == UINT32_MAX) return retcode; const char next = (response.Peek() ? *response.Peek() : 0); if (next == ',') return 0; if (next == ';') { response.GetChar(); // skip the semicolon std::string buffer; if (response.GetEscapedBinaryData(buffer)) { const uint64_t data_to_write = std::min(dst_len, buffer.size()); if (data_to_write > 0) memcpy(dst, &buffer[0], data_to_write); return data_to_write; } } } return 0; } uint64_t GDBRemoteCommunicationClient::WriteFile(lldb::user_id_t fd, uint64_t offset, const void *src, uint64_t src_len, Status &error) { lldb_private::StreamGDBRemote stream; stream.Printf("vFile:pwrite:%i,%" PRId64 ",", (int)fd, offset); stream.PutEscapedBytes(src, src_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() != 'F') { error.SetErrorStringWithFormat("write file failed"); return 0; } uint64_t bytes_written = response.GetU64(UINT64_MAX); if (bytes_written == UINT64_MAX) { error.SetErrorToGenericError(); if (response.GetChar() == ',') { int response_errno = response.GetS32(-1); if (response_errno > 0) error.SetError(response_errno, lldb::eErrorTypePOSIX); } return 0; } return bytes_written; } else { error.SetErrorString("failed to send vFile:pwrite packet"); } return 0; } Status GDBRemoteCommunicationClient::CreateSymlink(const FileSpec &src, const FileSpec &dst) { std::string src_path{src.GetPath(false)}, dst_path{dst.GetPath(false)}; Status error; lldb_private::StreamGDBRemote stream; stream.PutCString("vFile:symlink:"); // the unix symlink() command reverses its parameters where the dst if first, // so we follow suit here stream.PutCStringAsRawHex8(dst_path.c_str()); stream.PutChar(','); stream.PutCStringAsRawHex8(src_path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() == 'F') { uint32_t result = response.GetU32(UINT32_MAX); if (result != 0) { error.SetErrorToGenericError(); if (response.GetChar() == ',') { int response_errno = response.GetS32(-1); if (response_errno > 0) error.SetError(response_errno, lldb::eErrorTypePOSIX); } } } else { // Should have returned with 'F[,]' error.SetErrorStringWithFormat("symlink failed"); } } else { error.SetErrorString("failed to send vFile:symlink packet"); } return error; } Status GDBRemoteCommunicationClient::Unlink(const FileSpec &file_spec) { std::string path{file_spec.GetPath(false)}; Status error; lldb_private::StreamGDBRemote stream; stream.PutCString("vFile:unlink:"); // the unix symlink() command reverses its parameters where the dst if first, // so we follow suit here stream.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() == 'F') { uint32_t result = response.GetU32(UINT32_MAX); if (result != 0) { error.SetErrorToGenericError(); if (response.GetChar() == ',') { int response_errno = response.GetS32(-1); if (response_errno > 0) error.SetError(response_errno, lldb::eErrorTypePOSIX); } } } else { // Should have returned with 'F[,]' error.SetErrorStringWithFormat("unlink failed"); } } else { error.SetErrorString("failed to send vFile:unlink packet"); } return error; } // Extension of host I/O packets to get whether a file exists. bool GDBRemoteCommunicationClient::GetFileExists( const lldb_private::FileSpec &file_spec) { std::string path(file_spec.GetPath(false)); lldb_private::StreamString stream; stream.PutCString("vFile:exists:"); stream.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() != 'F') return false; if (response.GetChar() != ',') return false; bool retcode = (response.GetChar() != '0'); return retcode; } return false; } bool GDBRemoteCommunicationClient::CalculateMD5( const lldb_private::FileSpec &file_spec, uint64_t &high, uint64_t &low) { std::string path(file_spec.GetPath(false)); lldb_private::StreamString stream; stream.PutCString("vFile:MD5:"); stream.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() != 'F') return false; if (response.GetChar() != ',') return false; if (response.Peek() && *response.Peek() == 'x') return false; low = response.GetHexMaxU64(false, UINT64_MAX); high = response.GetHexMaxU64(false, UINT64_MAX); return true; } return false; } bool GDBRemoteCommunicationClient::AvoidGPackets(ProcessGDBRemote *process) { // Some targets have issues with g/G packets and we need to avoid using them if (m_avoid_g_packets == eLazyBoolCalculate) { if (process) { m_avoid_g_packets = eLazyBoolNo; const ArchSpec &arch = process->GetTarget().GetArchitecture(); if (arch.IsValid() && arch.GetTriple().getVendor() == llvm::Triple::Apple && arch.GetTriple().getOS() == llvm::Triple::IOS && arch.GetTriple().getArch() == llvm::Triple::aarch64) { m_avoid_g_packets = eLazyBoolYes; uint32_t gdb_server_version = GetGDBServerProgramVersion(); if (gdb_server_version != 0) { const char *gdb_server_name = GetGDBServerProgramName(); if (gdb_server_name && strcmp(gdb_server_name, "debugserver") == 0) { if (gdb_server_version >= 310) m_avoid_g_packets = eLazyBoolNo; } } } } } return m_avoid_g_packets == eLazyBoolYes; } DataBufferSP GDBRemoteCommunicationClient::ReadRegister(lldb::tid_t tid, uint32_t reg) { StreamString payload; payload.Printf("p%x", reg); StringExtractorGDBRemote response; if (SendThreadSpecificPacketAndWaitForResponse( tid, std::move(payload), response, false) != PacketResult::Success || !response.IsNormalResponse()) return nullptr; DataBufferSP buffer_sp( new DataBufferHeap(response.GetStringRef().size() / 2, 0)); response.GetHexBytes(buffer_sp->GetData(), '\xcc'); return buffer_sp; } DataBufferSP GDBRemoteCommunicationClient::ReadAllRegisters(lldb::tid_t tid) { StreamString payload; payload.PutChar('g'); StringExtractorGDBRemote response; if (SendThreadSpecificPacketAndWaitForResponse( tid, std::move(payload), response, false) != PacketResult::Success || !response.IsNormalResponse()) return nullptr; DataBufferSP buffer_sp( new DataBufferHeap(response.GetStringRef().size() / 2, 0)); response.GetHexBytes(buffer_sp->GetData(), '\xcc'); return buffer_sp; } bool GDBRemoteCommunicationClient::WriteRegister(lldb::tid_t tid, uint32_t reg_num, llvm::ArrayRef data) { StreamString payload; payload.Printf("P%x=", reg_num); payload.PutBytesAsRawHex8(data.data(), data.size(), endian::InlHostByteOrder(), endian::InlHostByteOrder()); StringExtractorGDBRemote response; return SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload), response, false) == PacketResult::Success && response.IsOKResponse(); } bool GDBRemoteCommunicationClient::WriteAllRegisters( lldb::tid_t tid, llvm::ArrayRef data) { StreamString payload; payload.PutChar('G'); payload.PutBytesAsRawHex8(data.data(), data.size(), endian::InlHostByteOrder(), endian::InlHostByteOrder()); StringExtractorGDBRemote response; return SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload), response, false) == PacketResult::Success && response.IsOKResponse(); } bool GDBRemoteCommunicationClient::SaveRegisterState(lldb::tid_t tid, uint32_t &save_id) { save_id = 0; // Set to invalid save ID if (m_supports_QSaveRegisterState == eLazyBoolNo) return false; m_supports_QSaveRegisterState = eLazyBoolYes; StreamString payload; payload.PutCString("QSaveRegisterState"); StringExtractorGDBRemote response; if (SendThreadSpecificPacketAndWaitForResponse( tid, std::move(payload), response, false) != PacketResult::Success) return false; if (response.IsUnsupportedResponse()) m_supports_QSaveRegisterState = eLazyBoolNo; const uint32_t response_save_id = response.GetU32(0); if (response_save_id == 0) return false; save_id = response_save_id; return true; } bool GDBRemoteCommunicationClient::RestoreRegisterState(lldb::tid_t tid, uint32_t save_id) { // We use the "m_supports_QSaveRegisterState" variable here because the // QSaveRegisterState and QRestoreRegisterState packets must both be supported // in // order to be useful if (m_supports_QSaveRegisterState == eLazyBoolNo) return false; StreamString payload; payload.Printf("QRestoreRegisterState:%u", save_id); StringExtractorGDBRemote response; if (SendThreadSpecificPacketAndWaitForResponse( tid, std::move(payload), response, false) != PacketResult::Success) return false; if (response.IsOKResponse()) return true; if (response.IsUnsupportedResponse()) m_supports_QSaveRegisterState = eLazyBoolNo; return false; } bool GDBRemoteCommunicationClient::SyncThreadState(lldb::tid_t tid) { if (!GetSyncThreadStateSupported()) return false; StreamString packet; StringExtractorGDBRemote response; packet.Printf("QSyncThreadState:%4.4" PRIx64 ";", tid); return SendPacketAndWaitForResponse(packet.GetString(), response, false) == GDBRemoteCommunication::PacketResult::Success && response.IsOKResponse(); +} + +lldb::user_id_t +GDBRemoteCommunicationClient::SendStartTracePacket(const TraceOptions &options, + Status &error) { + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + lldb::user_id_t ret_uid = LLDB_INVALID_UID; + + StreamGDBRemote escaped_packet; + escaped_packet.PutCString("jTraceStart:"); + + StructuredData::Dictionary json_packet; + json_packet.AddIntegerItem("type", options.getType()); + json_packet.AddIntegerItem("buffersize", options.getTraceBufferSize()); + json_packet.AddIntegerItem("metabuffersize", options.getMetaDataBufferSize()); + + if (options.getThreadID() != LLDB_INVALID_THREAD_ID) + json_packet.AddIntegerItem("threadid", options.getThreadID()); + + StructuredData::DictionarySP custom_params = options.getTraceParams(); + if (custom_params) + json_packet.AddItem("params", custom_params); + + StreamString json_string; + json_packet.Dump(json_string, false); + escaped_packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize()); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response, + true) == + GDBRemoteCommunication::PacketResult::Success) { + if (!response.IsNormalResponse()) { + error.SetError(response.GetError(), eErrorTypeGeneric); + LLDB_LOG(log, "Target does not support Tracing"); + } else { + ret_uid = response.GetHexMaxU64(false, LLDB_INVALID_UID); + } + } else { + LLDB_LOG(log, "failed to send packet"); + error.SetErrorStringWithFormat("failed to send packet: '%s'", + escaped_packet.GetData()); + } + return ret_uid; +} + +Status +GDBRemoteCommunicationClient::SendStopTracePacket(lldb::user_id_t uid, + lldb::tid_t thread_id) { + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + StringExtractorGDBRemote response; + Status error; + + StructuredData::Dictionary json_packet; + StreamGDBRemote escaped_packet; + StreamString json_string; + escaped_packet.PutCString("jTraceStop:"); + + json_packet.AddIntegerItem("traceid", uid); + + if (thread_id != LLDB_INVALID_THREAD_ID) + json_packet.AddIntegerItem("threadid", thread_id); + + json_packet.Dump(json_string, false); + + escaped_packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize()); + + if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response, + true) == + GDBRemoteCommunication::PacketResult::Success) { + if (!response.IsOKResponse()) { + error.SetError(response.GetError(), eErrorTypeGeneric); + LLDB_LOG(log, "stop tracing failed"); + } + } else { + LLDB_LOG(log, "failed to send packet"); + error.SetErrorStringWithFormat( + "failed to send packet: '%s' with error '%d'", escaped_packet.GetData(), + response.GetError()); + } + return error; +} + +Status GDBRemoteCommunicationClient::SendGetDataPacket( + lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, size_t offset) { + StreamGDBRemote escaped_packet; + escaped_packet.PutCString("jTraceBufferRead:"); + return SendGetTraceDataPacket(escaped_packet, uid, thread_id, buffer, offset); +} + +Status GDBRemoteCommunicationClient::SendGetMetaDataPacket( + lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, size_t offset) { + StreamGDBRemote escaped_packet; + escaped_packet.PutCString("jTraceMetaRead:"); + return SendGetTraceDataPacket(escaped_packet, uid, thread_id, buffer, offset); +} + +Status +GDBRemoteCommunicationClient::SendGetTraceConfigPacket(lldb::user_id_t uid, + TraceOptions &options) { + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + StringExtractorGDBRemote response; + Status error; + + StreamString json_string; + StreamGDBRemote escaped_packet; + escaped_packet.PutCString("jTraceConfigRead:"); + + StructuredData::Dictionary json_packet; + json_packet.AddIntegerItem("traceid", uid); + + if (options.getThreadID() != LLDB_INVALID_THREAD_ID) + json_packet.AddIntegerItem("threadid", options.getThreadID()); + + json_packet.Dump(json_string, false); + escaped_packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize()); + + if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response, + true) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsNormalResponse()) { + uint64_t type = std::numeric_limits::max(); + uint64_t buffersize = std::numeric_limits::max(); + uint64_t metabuffersize = std::numeric_limits::max(); + + auto json_object = StructuredData::ParseJSON(response.Peek()); + + if (!json_object || + json_object->GetType() != lldb::eStructuredDataTypeDictionary) { + error.SetErrorString("Invalid Configuration obtained"); + return error; + } + + auto json_dict = json_object->GetAsDictionary(); + + json_dict->GetValueForKeyAsInteger("metabuffersize", + metabuffersize); + options.setMetaDataBufferSize(metabuffersize); + + json_dict->GetValueForKeyAsInteger("buffersize", buffersize); + options.setTraceBufferSize(buffersize); + + json_dict->GetValueForKeyAsInteger("type", type); + options.setType(static_cast(type)); + + StructuredData::ObjectSP custom_params_sp = + json_dict->GetValueForKey("params"); + if (custom_params_sp) { + if (custom_params_sp->GetType() != + lldb::eStructuredDataTypeDictionary) { + error.SetErrorString("Invalid Configuration obtained"); + return error; + } else + options.setTraceParams( + static_pointer_cast( + custom_params_sp)); + } + } else { + error.SetError(response.GetError(), eErrorTypeGeneric); + } + } else { + LLDB_LOG(log, "failed to send packet"); + error.SetErrorStringWithFormat("failed to send packet: '%s'", + escaped_packet.GetData()); + } + return error; +} + +Status GDBRemoteCommunicationClient::SendGetTraceDataPacket( + StreamGDBRemote &packet, lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, size_t offset) { + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + Status error; + + StructuredData::Dictionary json_packet; + + json_packet.AddIntegerItem("traceid", uid); + json_packet.AddIntegerItem("offset", offset); + json_packet.AddIntegerItem("buffersize", buffer.size()); + + if (thread_id != LLDB_INVALID_THREAD_ID) + json_packet.AddIntegerItem("threadid", thread_id); + + StreamString json_string; + json_packet.Dump(json_string, false); + + packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize()); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response, true) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsNormalResponse()) { + size_t filled_size = response.GetHexBytesAvail(buffer); + buffer = llvm::MutableArrayRef(buffer.data(), filled_size); + } else { + error.SetError(response.GetError(), eErrorTypeGeneric); + buffer = buffer.slice(buffer.size()); + } + } else { + LLDB_LOG(log, "failed to send packet"); + error.SetErrorStringWithFormat("failed to send packet: '%s'", + packet.GetData()); + buffer = buffer.slice(buffer.size()); + } + return error; } bool GDBRemoteCommunicationClient::GetModuleInfo( const FileSpec &module_file_spec, const lldb_private::ArchSpec &arch_spec, ModuleSpec &module_spec) { if (!m_supports_qModuleInfo) return false; std::string module_path = module_file_spec.GetPath(false); if (module_path.empty()) return false; StreamString packet; packet.PutCString("qModuleInfo:"); packet.PutCStringAsRawHex8(module_path.c_str()); packet.PutCString(";"); const auto &triple = arch_spec.GetTriple().getTriple(); packet.PutCStringAsRawHex8(triple.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) != PacketResult::Success) return false; if (response.IsErrorResponse()) return false; if (response.IsUnsupportedResponse()) { m_supports_qModuleInfo = false; return false; } llvm::StringRef name; llvm::StringRef value; module_spec.Clear(); module_spec.GetFileSpec() = module_file_spec; while (response.GetNameColonValue(name, value)) { if (name == "uuid" || name == "md5") { StringExtractor extractor(value); std::string uuid; extractor.GetHexByteString(uuid); module_spec.GetUUID().SetFromCString(uuid.c_str(), uuid.size() / 2); } else if (name == "triple") { StringExtractor extractor(value); std::string triple; extractor.GetHexByteString(triple); module_spec.GetArchitecture().SetTriple(triple.c_str()); } else if (name == "file_offset") { uint64_t ival = 0; if (!value.getAsInteger(16, ival)) module_spec.SetObjectOffset(ival); } else if (name == "file_size") { uint64_t ival = 0; if (!value.getAsInteger(16, ival)) module_spec.SetObjectSize(ival); } else if (name == "file_path") { StringExtractor extractor(value); std::string path; extractor.GetHexByteString(path); module_spec.GetFileSpec() = FileSpec(path, false, arch_spec.GetTriple()); } } return true; } static llvm::Optional ParseModuleSpec(StructuredData::Dictionary *dict) { ModuleSpec result; if (!dict) return llvm::None; llvm::StringRef string; uint64_t integer; if (!dict->GetValueForKeyAsString("uuid", string)) return llvm::None; result.GetUUID().SetFromStringRef(string, string.size()); if (!dict->GetValueForKeyAsInteger("file_offset", integer)) return llvm::None; result.SetObjectOffset(integer); if (!dict->GetValueForKeyAsInteger("file_size", integer)) return llvm::None; result.SetObjectSize(integer); if (!dict->GetValueForKeyAsString("triple", string)) return llvm::None; result.GetArchitecture().SetTriple(string); if (!dict->GetValueForKeyAsString("file_path", string)) return llvm::None; result.GetFileSpec() = FileSpec(string, false, result.GetArchitecture().GetTriple()); return result; } llvm::Optional> GDBRemoteCommunicationClient::GetModulesInfo( llvm::ArrayRef module_file_specs, const llvm::Triple &triple) { if (!m_supports_jModulesInfo) return llvm::None; JSONArray::SP module_array_sp = std::make_shared(); for (const FileSpec &module_file_spec : module_file_specs) { JSONObject::SP module_sp = std::make_shared(); module_array_sp->AppendObject(module_sp); module_sp->SetObject( "file", std::make_shared(module_file_spec.GetPath(false))); module_sp->SetObject("triple", std::make_shared(triple.getTriple())); } StreamString unescaped_payload; unescaped_payload.PutCString("jModulesInfo:"); module_array_sp->Write(unescaped_payload); StreamGDBRemote payload; payload.PutEscapedBytes(unescaped_payload.GetString().data(), unescaped_payload.GetSize()); // Increase the timeout for jModulesInfo since this packet can take longer. ScopedTimeout timeout(*this, std::chrono::seconds(10)); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(payload.GetString(), response, false) != PacketResult::Success || response.IsErrorResponse()) return llvm::None; if (response.IsUnsupportedResponse()) { m_supports_jModulesInfo = false; return llvm::None; } StructuredData::ObjectSP response_object_sp = StructuredData::ParseJSON(response.GetStringRef()); if (!response_object_sp) return llvm::None; StructuredData::Array *response_array = response_object_sp->GetAsArray(); if (!response_array) return llvm::None; std::vector result; for (size_t i = 0; i < response_array->GetSize(); ++i) { if (llvm::Optional module_spec = ParseModuleSpec( response_array->GetItemAtIndex(i)->GetAsDictionary())) result.push_back(*module_spec); } return result; } // query the target remote for extended information using the qXfer packet // // example: object='features', annex='target.xml', out= // return: 'true' on success // 'false' on failure (err set) bool GDBRemoteCommunicationClient::ReadExtFeature( const lldb_private::ConstString object, const lldb_private::ConstString annex, std::string &out, lldb_private::Status &err) { std::stringstream output; StringExtractorGDBRemote chunk; uint64_t size = GetRemoteMaxPacketSize(); if (size == 0) size = 0x1000; size = size - 1; // Leave space for the 'm' or 'l' character in the response int offset = 0; bool active = true; // loop until all data has been read while (active) { // send query extended feature packet std::stringstream packet; packet << "qXfer:" << object.AsCString("") << ":read:" << annex.AsCString("") << ":" << std::hex << offset << "," << std::hex << size; GDBRemoteCommunication::PacketResult res = SendPacketAndWaitForResponse(packet.str(), chunk, false); if (res != GDBRemoteCommunication::PacketResult::Success) { err.SetErrorString("Error sending $qXfer packet"); return false; } const std::string &str = chunk.GetStringRef(); if (str.length() == 0) { // should have some data in chunk err.SetErrorString("Empty response from $qXfer packet"); return false; } // check packet code switch (str[0]) { // last chunk case ('l'): active = false; LLVM_FALLTHROUGH; // more chunks case ('m'): if (str.length() > 1) output << &str[1]; offset += size; break; // unknown chunk default: err.SetErrorString("Invalid continuation code from $qXfer packet"); return false; } } out = output.str(); err.Success(); return true; } // Notify the target that gdb is prepared to serve symbol lookup requests. // packet: "qSymbol::" // reply: // OK The target does not need to look up any (more) symbols. // qSymbol: The target requests the value of symbol sym_name (hex // encoded). // LLDB may provide the value by sending another qSymbol // packet // in the form of"qSymbol::". // // Three examples: // // lldb sends: qSymbol:: // lldb receives: OK // Remote gdb stub does not need to know the addresses of any symbols, lldb // does not // need to ask again in this session. // // lldb sends: qSymbol:: // lldb receives: qSymbol:64697370617463685f71756575655f6f666673657473 // lldb sends: qSymbol::64697370617463685f71756575655f6f666673657473 // lldb receives: OK // Remote gdb stub asks for address of 'dispatch_queue_offsets'. lldb does // not know // the address at this time. lldb needs to send qSymbol:: again when it has // more // solibs loaded. // // lldb sends: qSymbol:: // lldb receives: qSymbol:64697370617463685f71756575655f6f666673657473 // lldb sends: qSymbol:2bc97554:64697370617463685f71756575655f6f666673657473 // lldb receives: OK // Remote gdb stub asks for address of 'dispatch_queue_offsets'. lldb says // that it // is at address 0x2bc97554. Remote gdb stub sends 'OK' indicating that it // does not // need any more symbols. lldb does not need to ask again in this session. void GDBRemoteCommunicationClient::ServeSymbolLookups( lldb_private::Process *process) { // Set to true once we've resolved a symbol to an address for the remote stub. // If we get an 'OK' response after this, the remote stub doesn't need any // more // symbols and we can stop asking. bool symbol_response_provided = false; // Is this the initial qSymbol:: packet? bool first_qsymbol_query = true; if (m_supports_qSymbol && m_qSymbol_requests_done == false) { Lock lock(*this, false); if (lock) { StreamString packet; packet.PutCString("qSymbol::"); StringExtractorGDBRemote response; while (SendPacketAndWaitForResponseNoLock(packet.GetString(), response) == PacketResult::Success) { if (response.IsOKResponse()) { if (symbol_response_provided || first_qsymbol_query) { m_qSymbol_requests_done = true; } // We are done serving symbols requests return; } first_qsymbol_query = false; if (response.IsUnsupportedResponse()) { // qSymbol is not supported by the current GDB server we are connected // to m_supports_qSymbol = false; return; } else { llvm::StringRef response_str(response.GetStringRef()); if (response_str.startswith("qSymbol:")) { response.SetFilePos(strlen("qSymbol:")); std::string symbol_name; if (response.GetHexByteString(symbol_name)) { if (symbol_name.empty()) return; addr_t symbol_load_addr = LLDB_INVALID_ADDRESS; lldb_private::SymbolContextList sc_list; if (process->GetTarget().GetImages().FindSymbolsWithNameAndType( ConstString(symbol_name), eSymbolTypeAny, sc_list)) { const size_t num_scs = sc_list.GetSize(); for (size_t sc_idx = 0; sc_idx < num_scs && symbol_load_addr == LLDB_INVALID_ADDRESS; ++sc_idx) { SymbolContext sc; if (sc_list.GetContextAtIndex(sc_idx, sc)) { if (sc.symbol) { switch (sc.symbol->GetType()) { case eSymbolTypeInvalid: case eSymbolTypeAbsolute: case eSymbolTypeUndefined: case eSymbolTypeSourceFile: case eSymbolTypeHeaderFile: case eSymbolTypeObjectFile: case eSymbolTypeCommonBlock: case eSymbolTypeBlock: case eSymbolTypeLocal: case eSymbolTypeParam: case eSymbolTypeVariable: case eSymbolTypeVariableType: case eSymbolTypeLineEntry: case eSymbolTypeLineHeader: case eSymbolTypeScopeBegin: case eSymbolTypeScopeEnd: case eSymbolTypeAdditional: case eSymbolTypeCompiler: case eSymbolTypeInstrumentation: case eSymbolTypeTrampoline: break; case eSymbolTypeCode: case eSymbolTypeResolver: case eSymbolTypeData: case eSymbolTypeRuntime: case eSymbolTypeException: case eSymbolTypeObjCClass: case eSymbolTypeObjCMetaClass: case eSymbolTypeObjCIVar: case eSymbolTypeReExported: symbol_load_addr = sc.symbol->GetLoadAddress(&process->GetTarget()); break; } } } } } // This is the normal path where our symbol lookup was successful // and we want // to send a packet with the new symbol value and see if another // lookup needs to be // done. // Change "packet" to contain the requested symbol value and name packet.Clear(); packet.PutCString("qSymbol:"); if (symbol_load_addr != LLDB_INVALID_ADDRESS) { packet.Printf("%" PRIx64, symbol_load_addr); symbol_response_provided = true; } else { symbol_response_provided = false; } packet.PutCString(":"); packet.PutBytesAsRawHex8(symbol_name.data(), symbol_name.size()); continue; // go back to the while loop and send "packet" and wait // for another response } } } } // If we make it here, the symbol request packet response wasn't valid or // our symbol lookup failed so we must abort return; } else if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet( GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)) { log->Printf( "GDBRemoteCommunicationClient::%s: Didn't get sequence mutex.", __FUNCTION__); } } } StructuredData::Array * GDBRemoteCommunicationClient::GetSupportedStructuredDataPlugins() { if (!m_supported_async_json_packets_is_valid) { // Query the server for the array of supported asynchronous JSON // packets. m_supported_async_json_packets_is_valid = true; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); // Poll it now. StringExtractorGDBRemote response; const bool send_async = false; if (SendPacketAndWaitForResponse("qStructuredDataPlugins", response, send_async) == PacketResult::Success) { m_supported_async_json_packets_sp = StructuredData::ParseJSON(response.GetStringRef()); if (m_supported_async_json_packets_sp && !m_supported_async_json_packets_sp->GetAsArray()) { // We were returned something other than a JSON array. This // is invalid. Clear it out. if (log) log->Printf("GDBRemoteCommunicationClient::%s(): " "QSupportedAsyncJSONPackets returned invalid " "result: %s", __FUNCTION__, response.GetStringRef().c_str()); m_supported_async_json_packets_sp.reset(); } } else { if (log) log->Printf("GDBRemoteCommunicationClient::%s(): " "QSupportedAsyncJSONPackets unsupported", __FUNCTION__); } if (log && m_supported_async_json_packets_sp) { StreamString stream; m_supported_async_json_packets_sp->Dump(stream); log->Printf("GDBRemoteCommunicationClient::%s(): supported async " "JSON packets: %s", __FUNCTION__, stream.GetData()); } } return m_supported_async_json_packets_sp ? m_supported_async_json_packets_sp->GetAsArray() : nullptr; } Status GDBRemoteCommunicationClient::SendSignalsToIgnore( llvm::ArrayRef signals) { // Format packet: // QPassSignals:;...; auto range = llvm::make_range(signals.begin(), signals.end()); std::string packet = formatv("QPassSignals:{0:$[;]@(x-2)}", range).str(); StringExtractorGDBRemote response; auto send_status = SendPacketAndWaitForResponse(packet, response, false); if (send_status != GDBRemoteCommunication::PacketResult::Success) return Status("Sending QPassSignals packet failed"); if (response.IsOKResponse()) { return Status(); } else { return Status("Unknown error happened during sending QPassSignals packet."); } } Status GDBRemoteCommunicationClient::ConfigureRemoteStructuredData( const ConstString &type_name, const StructuredData::ObjectSP &config_sp) { Status error; if (type_name.GetLength() == 0) { error.SetErrorString("invalid type_name argument"); return error; } // Build command: Configure{type_name}: serialized config // data. StreamGDBRemote stream; stream.PutCString("QConfigure"); stream.PutCString(type_name.AsCString()); stream.PutChar(':'); if (config_sp) { // Gather the plain-text version of the configuration data. StreamString unescaped_stream; config_sp->Dump(unescaped_stream); unescaped_stream.Flush(); // Add it to the stream in escaped fashion. stream.PutEscapedBytes(unescaped_stream.GetString().data(), unescaped_stream.GetSize()); } stream.Flush(); // Send the packet. const bool send_async = false; StringExtractorGDBRemote response; auto result = SendPacketAndWaitForResponse(stream.GetString(), response, send_async); if (result == PacketResult::Success) { // We failed if the config result comes back other than OK. if (strcmp(response.GetStringRef().c_str(), "OK") == 0) { // Okay! error.Clear(); } else { error.SetErrorStringWithFormat("configuring StructuredData feature " "%s failed with error %s", type_name.AsCString(), response.GetStringRef().c_str()); } } else { // Can we get more data here on the failure? error.SetErrorStringWithFormat("configuring StructuredData feature %s " "failed when sending packet: " "PacketResult=%d", type_name.AsCString(), (int)result); } return error; } void GDBRemoteCommunicationClient::OnRunPacketSent(bool first) { GDBRemoteClientBase::OnRunPacketSent(first); m_curr_tid = LLDB_INVALID_THREAD_ID; } Index: vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h (revision 319149) +++ vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h (revision 319150) @@ -1,597 +1,618 @@ //===-- GDBRemoteCommunicationClient.h --------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_GDBRemoteCommunicationClient_h_ #define liblldb_GDBRemoteCommunicationClient_h_ #include "GDBRemoteClientBase.h" // C Includes // C++ Includes #include #include #include #include #include // Other libraries and framework includes // Project includes #include "lldb/Core/ArchSpec.h" #include "lldb/Core/StructuredData.h" #include "lldb/Target/Process.h" +#include "lldb/Utility/StreamGDBRemote.h" #include "llvm/ADT/Optional.h" namespace lldb_private { namespace process_gdb_remote { class GDBRemoteCommunicationClient : public GDBRemoteClientBase { public: GDBRemoteCommunicationClient(); ~GDBRemoteCommunicationClient() override; //------------------------------------------------------------------ // After connecting, send the handshake to the server to make sure // we are communicating with it. //------------------------------------------------------------------ bool HandshakeWithServer(Status *error_ptr); // For packets which specify a range of output to be returned, // return all of the output via a series of request packets of the form // 0, // , // *2, // *3, // ... // until a "$l..." packet is received, indicating the end. // (size is in hex; this format is used by a standard gdbserver to // return the given portion of the output specified by ; // for example, "qXfer:libraries-svr4:read::fff,1000" means // "return a chunk of the xml description file for shared // library load addresses, where the chunk starts at offset 0xfff // and continues for 0x1000 bytes"). // Concatenate the resulting server response packets together and // return in response_string. If any packet fails, the return value // indicates that failure and the returned string value is undefined. PacketResult SendPacketsAndConcatenateResponses(const char *send_payload_prefix, std::string &response_string); bool GetThreadSuffixSupported(); // This packet is usually sent first and the boolean return value // indicates if the packet was send and any response was received // even in the response is UNIMPLEMENTED. If the packet failed to // get a response, then false is returned. This quickly tells us // if we were able to connect and communicate with the remote GDB // server bool QueryNoAckModeSupported(); void GetListThreadsInStopReplySupported(); lldb::pid_t GetCurrentProcessID(bool allow_lazy = true); bool GetLaunchSuccess(std::string &error_str); bool LaunchGDBServer(const char *remote_accept_hostname, lldb::pid_t &pid, uint16_t &port, std::string &socket_name); size_t QueryGDBServer( std::vector> &connection_urls); bool KillSpawnedProcess(lldb::pid_t pid); //------------------------------------------------------------------ /// Sends a GDB remote protocol 'A' packet that delivers program /// arguments to the remote server. /// /// @param[in] argv /// A NULL terminated array of const C strings to use as the /// arguments. /// /// @return /// Zero if the response was "OK", a positive value if the /// the response was "Exx" where xx are two hex digits, or /// -1 if the call is unsupported or any other unexpected /// response was received. //------------------------------------------------------------------ int SendArgumentsPacket(const ProcessLaunchInfo &launch_info); //------------------------------------------------------------------ /// Sends a "QEnvironment:NAME=VALUE" packet that will build up the /// environment that will get used when launching an application /// in conjunction with the 'A' packet. This function can be called /// multiple times in a row in order to pass on the desired /// environment that the inferior should be launched with. /// /// @param[in] name_equal_value /// A NULL terminated C string that contains a single environment /// in the format "NAME=VALUE". /// /// @return /// Zero if the response was "OK", a positive value if the /// the response was "Exx" where xx are two hex digits, or /// -1 if the call is unsupported or any other unexpected /// response was received. //------------------------------------------------------------------ int SendEnvironmentPacket(char const *name_equal_value); int SendLaunchArchPacket(const char *arch); int SendLaunchEventDataPacket(const char *data, bool *was_supported = nullptr); //------------------------------------------------------------------ /// Sends a "vAttach:PID" where PID is in hex. /// /// @param[in] pid /// A process ID for the remote gdb server to attach to. /// /// @param[out] response /// The response received from the gdb server. If the return /// value is zero, \a response will contain a stop reply /// packet. /// /// @return /// Zero if the attach was successful, or an error indicating /// an error code. //------------------------------------------------------------------ int SendAttach(lldb::pid_t pid, StringExtractorGDBRemote &response); //------------------------------------------------------------------ /// Sends a GDB remote protocol 'I' packet that delivers stdin /// data to the remote process. /// /// @param[in] data /// A pointer to stdin data. /// /// @param[in] data_len /// The number of bytes available at \a data. /// /// @return /// Zero if the attach was successful, or an error indicating /// an error code. //------------------------------------------------------------------ int SendStdinNotification(const char *data, size_t data_len); //------------------------------------------------------------------ /// Sets the path to use for stdin/out/err for a process /// that will be launched with the 'A' packet. /// /// @param[in] path /// The path to use for stdin/out/err /// /// @return /// Zero if the for success, or an error code for failure. //------------------------------------------------------------------ int SetSTDIN(const FileSpec &file_spec); int SetSTDOUT(const FileSpec &file_spec); int SetSTDERR(const FileSpec &file_spec); //------------------------------------------------------------------ /// Sets the disable ASLR flag to \a enable for a process that will /// be launched with the 'A' packet. /// /// @param[in] enable /// A boolean value indicating whether to disable ASLR or not. /// /// @return /// Zero if the for success, or an error code for failure. //------------------------------------------------------------------ int SetDisableASLR(bool enable); //------------------------------------------------------------------ /// Sets the DetachOnError flag to \a enable for the process controlled by the /// stub. /// /// @param[in] enable /// A boolean value indicating whether to detach on error or not. /// /// @return /// Zero if the for success, or an error code for failure. //------------------------------------------------------------------ int SetDetachOnError(bool enable); //------------------------------------------------------------------ /// Sets the working directory to \a path for a process that will /// be launched with the 'A' packet for non platform based /// connections. If this packet is sent to a GDB server that /// implements the platform, it will change the current working /// directory for the platform process. /// /// @param[in] working_dir /// The path to a directory to use when launching our process /// /// @return /// Zero if the for success, or an error code for failure. //------------------------------------------------------------------ int SetWorkingDir(const FileSpec &working_dir); //------------------------------------------------------------------ /// Gets the current working directory of a remote platform GDB /// server. /// /// @param[out] working_dir /// The current working directory on the remote platform. /// /// @return /// Boolean for success //------------------------------------------------------------------ bool GetWorkingDir(FileSpec &working_dir); lldb::addr_t AllocateMemory(size_t size, uint32_t permissions); bool DeallocateMemory(lldb::addr_t addr); Status Detach(bool keep_stopped); Status GetMemoryRegionInfo(lldb::addr_t addr, MemoryRegionInfo &range_info); Status GetWatchpointSupportInfo(uint32_t &num); Status GetWatchpointSupportInfo(uint32_t &num, bool &after, const ArchSpec &arch); Status GetWatchpointsTriggerAfterInstruction(bool &after, const ArchSpec &arch); const ArchSpec &GetHostArchitecture(); std::chrono::seconds GetHostDefaultPacketTimeout(); const ArchSpec &GetProcessArchitecture(); void GetRemoteQSupported(); bool GetVContSupported(char flavor); bool GetpPacketSupported(lldb::tid_t tid); bool GetxPacketSupported(); bool GetVAttachOrWaitSupported(); bool GetSyncThreadStateSupported(); void ResetDiscoverableSettings(bool did_exec); bool GetHostInfo(bool force = false); bool GetDefaultThreadId(lldb::tid_t &tid); bool GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update); bool GetOSBuildString(std::string &s); bool GetOSKernelDescription(std::string &s); ArchSpec GetSystemArchitecture(); bool GetHostname(std::string &s); lldb::addr_t GetShlibInfoAddr(); bool GetSupportsThreadSuffix(); bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info); uint32_t FindProcesses(const ProcessInstanceInfoMatch &process_match_info, ProcessInstanceInfoList &process_infos); bool GetUserName(uint32_t uid, std::string &name); bool GetGroupName(uint32_t gid, std::string &name); bool HasFullVContSupport() { return GetVContSupported('A'); } bool HasAnyVContSupport() { return GetVContSupported('a'); } bool GetStopReply(StringExtractorGDBRemote &response); bool GetThreadStopInfo(lldb::tid_t tid, StringExtractorGDBRemote &response); bool SupportsGDBStoppointPacket(GDBStoppointType type) { switch (type) { case eBreakpointSoftware: return m_supports_z0; case eBreakpointHardware: return m_supports_z1; case eWatchpointWrite: return m_supports_z2; case eWatchpointRead: return m_supports_z3; case eWatchpointReadWrite: return m_supports_z4; default: return false; } } uint8_t SendGDBStoppointTypePacket( GDBStoppointType type, // Type of breakpoint or watchpoint bool insert, // Insert or remove? lldb::addr_t addr, // Address of breakpoint or watchpoint uint32_t length); // Byte Size of breakpoint or watchpoint bool SetNonStopMode(const bool enable); void TestPacketSpeed(const uint32_t num_packets, uint32_t max_send, uint32_t max_recv, uint64_t recv_amount, bool json, Stream &strm); // This packet is for testing the speed of the interface only. Both // the client and server need to support it, but this allows us to // measure the packet speed without any other work being done on the // other end and avoids any of that work affecting the packet send // and response times. bool SendSpeedTestPacket(uint32_t send_size, uint32_t recv_size); bool SetCurrentThread(uint64_t tid); bool SetCurrentThreadForRun(uint64_t tid); bool GetQXferAuxvReadSupported(); bool GetQXferLibrariesReadSupported(); bool GetQXferLibrariesSVR4ReadSupported(); uint64_t GetRemoteMaxPacketSize(); bool GetEchoSupported(); bool GetQPassSignalsSupported(); bool GetAugmentedLibrariesSVR4ReadSupported(); bool GetQXferFeaturesReadSupported(); LazyBool SupportsAllocDeallocMemory() // const { // Uncomment this to have lldb pretend the debug server doesn't respond to // alloc/dealloc memory packets. // m_supports_alloc_dealloc_memory = lldb_private::eLazyBoolNo; return m_supports_alloc_dealloc_memory; } size_t GetCurrentThreadIDs(std::vector &thread_ids, bool &sequence_mutex_unavailable); lldb::user_id_t OpenFile(const FileSpec &file_spec, uint32_t flags, mode_t mode, Status &error); bool CloseFile(lldb::user_id_t fd, Status &error); lldb::user_id_t GetFileSize(const FileSpec &file_spec); Status GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions); Status SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions); uint64_t ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst, uint64_t dst_len, Status &error); uint64_t WriteFile(lldb::user_id_t fd, uint64_t offset, const void *src, uint64_t src_len, Status &error); Status CreateSymlink(const FileSpec &src, const FileSpec &dst); Status Unlink(const FileSpec &file_spec); Status MakeDirectory(const FileSpec &file_spec, uint32_t mode); bool GetFileExists(const FileSpec &file_spec); Status RunShellCommand( const char *command, // Shouldn't be nullptr const FileSpec &working_dir, // Pass empty FileSpec to use the current // working directory int *status_ptr, // Pass nullptr if you don't want the process exit status int *signo_ptr, // Pass nullptr if you don't want the signal that caused // the process to exit std::string *command_output, // Pass nullptr if you don't want the command output uint32_t timeout_sec); // Timeout in seconds to wait for shell program to // finish bool CalculateMD5(const FileSpec &file_spec, uint64_t &high, uint64_t &low); lldb::DataBufferSP ReadRegister( lldb::tid_t tid, uint32_t reg_num); // Must be the eRegisterKindProcessPlugin register number lldb::DataBufferSP ReadAllRegisters(lldb::tid_t tid); bool WriteRegister(lldb::tid_t tid, uint32_t reg_num, // eRegisterKindProcessPlugin register number llvm::ArrayRef data); bool WriteAllRegisters(lldb::tid_t tid, llvm::ArrayRef data); bool SaveRegisterState(lldb::tid_t tid, uint32_t &save_id); bool RestoreRegisterState(lldb::tid_t tid, uint32_t save_id); bool SyncThreadState(lldb::tid_t tid); const char *GetGDBServerProgramName(); uint32_t GetGDBServerProgramVersion(); bool AvoidGPackets(ProcessGDBRemote *process); StructuredData::ObjectSP GetThreadsInfo(); bool GetThreadExtendedInfoSupported(); bool GetLoadedDynamicLibrariesInfosSupported(); bool GetSharedCacheInfoSupported(); bool GetModuleInfo(const FileSpec &module_file_spec, const ArchSpec &arch_spec, ModuleSpec &module_spec); llvm::Optional> GetModulesInfo(llvm::ArrayRef module_file_specs, const llvm::Triple &triple); bool ReadExtFeature(const lldb_private::ConstString object, const lldb_private::ConstString annex, std::string &out, lldb_private::Status &err); void ServeSymbolLookups(lldb_private::Process *process); // Sends QPassSignals packet to the server with given signals to ignore. Status SendSignalsToIgnore(llvm::ArrayRef signals); //------------------------------------------------------------------ /// Return the feature set supported by the gdb-remote server. /// /// This method returns the remote side's response to the qSupported /// packet. The response is the complete string payload returned /// to the client. /// /// @return /// The string returned by the server to the qSupported query. //------------------------------------------------------------------ const std::string &GetServerSupportedFeatures() const { return m_qSupported_response; } //------------------------------------------------------------------ /// Return the array of async JSON packet types supported by the remote. /// /// This method returns the remote side's array of supported JSON /// packet types as a list of type names. Each of the results are /// expected to have an Enable{type_name} command to enable and configure /// the related feature. Each type_name for an enabled feature will /// possibly send async-style packets that contain a payload of a /// binhex-encoded JSON dictionary. The dictionary will have a /// string field named 'type', that contains the type_name of the /// supported packet type. /// /// There is a Plugin category called structured-data plugins. /// A plugin indicates whether it knows how to handle a type_name. /// If so, it can be used to process the async JSON packet. /// /// @return /// The string returned by the server to the qSupported query. //------------------------------------------------------------------ lldb_private::StructuredData::Array *GetSupportedStructuredDataPlugins(); //------------------------------------------------------------------ /// Configure a StructuredData feature on the remote end. /// /// @see \b Process::ConfigureStructuredData(...) for details. //------------------------------------------------------------------ Status ConfigureRemoteStructuredData(const ConstString &type_name, const StructuredData::ObjectSP &config_sp); + lldb::user_id_t SendStartTracePacket(const TraceOptions &options, + Status &error); + + Status SendStopTracePacket(lldb::user_id_t uid, lldb::tid_t thread_id); + + Status SendGetDataPacket(lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, + size_t offset = 0); + + Status SendGetMetaDataPacket(lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, + size_t offset = 0); + + Status SendGetTraceConfigPacket(lldb::user_id_t uid, TraceOptions &options); + protected: LazyBool m_supports_not_sending_acks; LazyBool m_supports_thread_suffix; LazyBool m_supports_threads_in_stop_reply; LazyBool m_supports_vCont_all; LazyBool m_supports_vCont_any; LazyBool m_supports_vCont_c; LazyBool m_supports_vCont_C; LazyBool m_supports_vCont_s; LazyBool m_supports_vCont_S; LazyBool m_qHostInfo_is_valid; LazyBool m_curr_pid_is_valid; LazyBool m_qProcessInfo_is_valid; LazyBool m_qGDBServerVersion_is_valid; LazyBool m_supports_alloc_dealloc_memory; LazyBool m_supports_memory_region_info; LazyBool m_supports_watchpoint_support_info; LazyBool m_supports_detach_stay_stopped; LazyBool m_watchpoints_trigger_after_instruction; LazyBool m_attach_or_wait_reply; LazyBool m_prepare_for_reg_writing_reply; LazyBool m_supports_p; LazyBool m_supports_x; LazyBool m_avoid_g_packets; LazyBool m_supports_QSaveRegisterState; LazyBool m_supports_qXfer_auxv_read; LazyBool m_supports_qXfer_libraries_read; LazyBool m_supports_qXfer_libraries_svr4_read; LazyBool m_supports_qXfer_features_read; LazyBool m_supports_augmented_libraries_svr4_read; LazyBool m_supports_jThreadExtendedInfo; LazyBool m_supports_jLoadedDynamicLibrariesInfos; LazyBool m_supports_jGetSharedCacheInfo; LazyBool m_supports_QPassSignals; bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1, m_supports_qUserName : 1, m_supports_qGroupName : 1, m_supports_qThreadStopInfo : 1, m_supports_z0 : 1, m_supports_z1 : 1, m_supports_z2 : 1, m_supports_z3 : 1, m_supports_z4 : 1, m_supports_QEnvironment : 1, m_supports_QEnvironmentHexEncoded : 1, m_supports_qSymbol : 1, m_qSymbol_requests_done : 1, m_supports_qModuleInfo : 1, m_supports_jThreadsInfo : 1, m_supports_jModulesInfo : 1; lldb::pid_t m_curr_pid; lldb::tid_t m_curr_tid; // Current gdb remote protocol thread index for all // other operations lldb::tid_t m_curr_tid_run; // Current gdb remote protocol thread index for // continue, step, etc uint32_t m_num_supported_hardware_watchpoints; ArchSpec m_host_arch; ArchSpec m_process_arch; uint32_t m_os_version_major; uint32_t m_os_version_minor; uint32_t m_os_version_update; std::string m_os_build; std::string m_os_kernel; std::string m_hostname; std::string m_gdb_server_name; // from reply to qGDBServerVersion, empty if // qGDBServerVersion is not supported uint32_t m_gdb_server_version; // from reply to qGDBServerVersion, zero if // qGDBServerVersion is not supported std::chrono::seconds m_default_packet_timeout; uint64_t m_max_packet_size; // as returned by qSupported std::string m_qSupported_response; // the complete response to qSupported bool m_supported_async_json_packets_is_valid; lldb_private::StructuredData::ObjectSP m_supported_async_json_packets_sp; bool GetCurrentProcessInfo(bool allow_lazy_pid = true); bool GetGDBServerVersion(); // Given the list of compression types that the remote debug stub can support, // possibly enable compression if we find an encoding we can handle. void MaybeEnableCompression(std::vector supported_compressions); bool DecodeProcessInfoResponse(StringExtractorGDBRemote &response, ProcessInstanceInfo &process_info); void OnRunPacketSent(bool first) override; PacketResult SendThreadSpecificPacketAndWaitForResponse( lldb::tid_t tid, StreamString &&payload, StringExtractorGDBRemote &response, bool send_async); + + Status SendGetTraceDataPacket(StreamGDBRemote &packet, lldb::user_id_t uid, + lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, + size_t offset); private: DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationClient); }; } // namespace process_gdb_remote } // namespace lldb_private #endif // liblldb_GDBRemoteCommunicationClient_h_ Index: vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp (revision 319149) +++ vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp (revision 319150) @@ -1,3228 +1,3469 @@ //===-- GDBRemoteCommunicationServerLLGS.cpp --------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include "lldb/Host/Config.h" #include "GDBRemoteCommunicationServerLLGS.h" #include "lldb/Utility/StreamGDBRemote.h" // C Includes // C++ Includes #include #include #include // Other libraries and framework includes #include "lldb/Core/RegisterValue.h" #include "lldb/Core/State.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Debug.h" #include "lldb/Host/File.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Host/common/NativeRegisterContext.h" #include "lldb/Host/common/NativeThreadProtocol.h" #include "lldb/Interpreter/Args.h" #include "lldb/Target/FileAction.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/Endian.h" #include "lldb/Utility/JSON.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/UriParser.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/ScopedPrinter.h" // Project includes #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "Utility/StringExtractorGDBRemote.h" using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_gdb_remote; using namespace llvm; //---------------------------------------------------------------------- // GDBRemote Errors //---------------------------------------------------------------------- namespace { enum GDBRemoteServerError { // Set to the first unused error number in literal form below eErrorFirst = 29, eErrorNoProcess = eErrorFirst, eErrorResume, eErrorExitStatus }; } //---------------------------------------------------------------------- // GDBRemoteCommunicationServerLLGS constructor //---------------------------------------------------------------------- GDBRemoteCommunicationServerLLGS::GDBRemoteCommunicationServerLLGS( MainLoop &mainloop) : GDBRemoteCommunicationServerCommon("gdb-remote.server", "gdb-remote.server.rx_packet"), m_mainloop(mainloop), m_current_tid(LLDB_INVALID_THREAD_ID), m_continue_tid(LLDB_INVALID_THREAD_ID), m_debugged_process_mutex(), m_debugged_process_sp(), m_stdio_communication("process.stdio"), m_inferior_prev_state(StateType::eStateInvalid), m_saved_registers_map(), m_next_saved_registers_id(1), m_handshake_completed(false) { RegisterPacketHandlers(); } void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() { RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_C, &GDBRemoteCommunicationServerLLGS::Handle_C); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_c, &GDBRemoteCommunicationServerLLGS::Handle_c); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_D, &GDBRemoteCommunicationServerLLGS::Handle_D); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_H, &GDBRemoteCommunicationServerLLGS::Handle_H); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_I, &GDBRemoteCommunicationServerLLGS::Handle_I); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_interrupt, &GDBRemoteCommunicationServerLLGS::Handle_interrupt); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_m, &GDBRemoteCommunicationServerLLGS::Handle_memory_read); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_M, &GDBRemoteCommunicationServerLLGS::Handle_M); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_p, &GDBRemoteCommunicationServerLLGS::Handle_p); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_P, &GDBRemoteCommunicationServerLLGS::Handle_P); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qC, &GDBRemoteCommunicationServerLLGS::Handle_qC); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qfThreadInfo, &GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qFileLoadAddress, &GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir, &GDBRemoteCommunicationServerLLGS::Handle_qGetWorkingDir); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qMemoryRegionInfo, &GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qMemoryRegionInfoSupported, &GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfoSupported); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qProcessInfo, &GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qRegisterInfo, &GDBRemoteCommunicationServerLLGS::Handle_qRegisterInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_QRestoreRegisterState, &GDBRemoteCommunicationServerLLGS::Handle_QRestoreRegisterState); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_QSaveRegisterState, &GDBRemoteCommunicationServerLLGS::Handle_QSaveRegisterState); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_QSetDisableASLR, &GDBRemoteCommunicationServerLLGS::Handle_QSetDisableASLR); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir, &GDBRemoteCommunicationServerLLGS::Handle_QSetWorkingDir); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qsThreadInfo, &GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qThreadStopInfo, &GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_jThreadsInfo, &GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qWatchpointSupportInfo, &GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qXfer_auxv_read, &GDBRemoteCommunicationServerLLGS::Handle_qXfer_auxv_read); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_s, &GDBRemoteCommunicationServerLLGS::Handle_s); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_stop_reason, &GDBRemoteCommunicationServerLLGS::Handle_stop_reason); // ? RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_vAttach, &GDBRemoteCommunicationServerLLGS::Handle_vAttach); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_vCont, &GDBRemoteCommunicationServerLLGS::Handle_vCont); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_vCont_actions, &GDBRemoteCommunicationServerLLGS::Handle_vCont_actions); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_x, &GDBRemoteCommunicationServerLLGS::Handle_memory_read); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_Z, &GDBRemoteCommunicationServerLLGS::Handle_Z); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_z, &GDBRemoteCommunicationServerLLGS::Handle_z); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_QPassSignals, &GDBRemoteCommunicationServerLLGS::Handle_QPassSignals); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jTraceStart, + &GDBRemoteCommunicationServerLLGS::Handle_jTraceStart); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jTraceBufferRead, + &GDBRemoteCommunicationServerLLGS::Handle_jTraceRead); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jTraceMetaRead, + &GDBRemoteCommunicationServerLLGS::Handle_jTraceRead); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jTraceStop, + &GDBRemoteCommunicationServerLLGS::Handle_jTraceStop); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jTraceConfigRead, + &GDBRemoteCommunicationServerLLGS::Handle_jTraceConfigRead); + RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_k, [this](StringExtractorGDBRemote packet, Status &error, bool &interrupt, bool &quit) { quit = true; return this->Handle_k(packet); }); } Status GDBRemoteCommunicationServerLLGS::SetLaunchArguments(const char *const args[], int argc) { if ((argc < 1) || !args || !args[0] || !args[0][0]) return Status("%s: no process command line specified to launch", __FUNCTION__); m_process_launch_info.SetArguments(const_cast(args), true); return Status(); } Status GDBRemoteCommunicationServerLLGS::SetLaunchFlags(unsigned int launch_flags) { m_process_launch_info.GetFlags().Set(launch_flags); return Status(); } Status GDBRemoteCommunicationServerLLGS::LaunchProcess() { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (!m_process_launch_info.GetArguments().GetArgumentCount()) return Status("%s: no process command line specified to launch", __FUNCTION__); const bool should_forward_stdio = m_process_launch_info.GetFileActionForFD(STDIN_FILENO) == nullptr || m_process_launch_info.GetFileActionForFD(STDOUT_FILENO) == nullptr || m_process_launch_info.GetFileActionForFD(STDERR_FILENO) == nullptr; m_process_launch_info.SetLaunchInSeparateProcessGroup(true); m_process_launch_info.GetFlags().Set(eLaunchFlagDebug); const bool default_to_use_pty = true; m_process_launch_info.FinalizeFileActions(nullptr, default_to_use_pty); Status error; { std::lock_guard guard(m_debugged_process_mutex); assert(!m_debugged_process_sp && "lldb-server creating debugged " "process but one already exists"); error = NativeProcessProtocol::Launch(m_process_launch_info, *this, m_mainloop, m_debugged_process_sp); } if (!error.Success()) { fprintf(stderr, "%s: failed to launch executable %s", __FUNCTION__, m_process_launch_info.GetArguments().GetArgumentAtIndex(0)); return error; } // Handle mirroring of inferior stdout/stderr over the gdb-remote protocol // as needed. // llgs local-process debugging may specify PTY paths, which will make these // file actions non-null // process launch -i/e/o will also make these file actions non-null // nullptr means that the traffic is expected to flow over gdb-remote protocol if (should_forward_stdio) { // nullptr means it's not redirected to file or pty (in case of LLGS local) // at least one of stdio will be transferred pty<->gdb-remote // we need to give the pty master handle to this object to read and/or write if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " setting up stdout/stderr redirection via $O gdb-remote commands", __FUNCTION__, m_debugged_process_sp->GetID()); // Setup stdout/stderr mapping from inferior to $O auto terminal_fd = m_debugged_process_sp->GetTerminalFileDescriptor(); if (terminal_fd >= 0) { if (log) log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s setting " "inferior STDIO fd to %d", __FUNCTION__, terminal_fd); error = SetSTDIOFileDescriptor(terminal_fd); if (error.Fail()) return error; } else { if (log) log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s ignoring " "inferior STDIO since terminal fd reported as %d", __FUNCTION__, terminal_fd); } } else { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " skipping stdout/stderr redirection via $O: inferior will " "communicate over client-provided file descriptors", __FUNCTION__, m_debugged_process_sp->GetID()); } printf("Launched '%s' as process %" PRIu64 "...\n", m_process_launch_info.GetArguments().GetArgumentAtIndex(0), m_process_launch_info.GetProcessID()); return error; } Status GDBRemoteCommunicationServerLLGS::AttachToProcess(lldb::pid_t pid) { Status error; Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64, __FUNCTION__, pid); // Before we try to attach, make sure we aren't already monitoring something // else. if (m_debugged_process_sp && m_debugged_process_sp->GetID() != LLDB_INVALID_PROCESS_ID) return Status("cannot attach to a process %" PRIu64 " when another process with pid %" PRIu64 " is being debugged.", pid, m_debugged_process_sp->GetID()); // Try to attach. error = NativeProcessProtocol::Attach(pid, *this, m_mainloop, m_debugged_process_sp); if (!error.Success()) { fprintf(stderr, "%s: failed to attach to process %" PRIu64 ": %s", __FUNCTION__, pid, error.AsCString()); return error; } // Setup stdout/stderr mapping from inferior. auto terminal_fd = m_debugged_process_sp->GetTerminalFileDescriptor(); if (terminal_fd >= 0) { if (log) log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s setting " "inferior STDIO fd to %d", __FUNCTION__, terminal_fd); error = SetSTDIOFileDescriptor(terminal_fd); if (error.Fail()) return error; } else { if (log) log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s ignoring " "inferior STDIO since terminal fd reported as %d", __FUNCTION__, terminal_fd); } printf("Attached to process %" PRIu64 "...\n", pid); return error; } void GDBRemoteCommunicationServerLLGS::InitializeDelegate( NativeProcessProtocol *process) { assert(process && "process cannot be NULL"); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) { log->Printf("GDBRemoteCommunicationServerLLGS::%s called with " "NativeProcessProtocol pid %" PRIu64 ", current state: %s", __FUNCTION__, process->GetID(), StateAsCString(process->GetState())); } } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::SendWResponse( NativeProcessProtocol *process) { assert(process && "process cannot be NULL"); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // send W notification ExitType exit_type = ExitType::eExitTypeInvalid; int return_code = 0; std::string exit_description; const bool got_exit_info = process->GetExitStatus(&exit_type, &return_code, exit_description); if (!got_exit_info) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 ", failed to retrieve process exit status", __FUNCTION__, process->GetID()); StreamGDBRemote response; response.PutChar('E'); response.PutHex8(GDBRemoteServerError::eErrorExitStatus); return SendPacketNoLock(response.GetString()); } else { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 ", returning exit type %d, return code %d [%s]", __FUNCTION__, process->GetID(), exit_type, return_code, exit_description.c_str()); StreamGDBRemote response; char return_type_code; switch (exit_type) { case ExitType::eExitTypeExit: return_type_code = 'W'; break; case ExitType::eExitTypeSignal: return_type_code = 'X'; break; case ExitType::eExitTypeStop: return_type_code = 'S'; break; case ExitType::eExitTypeInvalid: return_type_code = 'E'; break; } response.PutChar(return_type_code); // POSIX exit status limited to unsigned 8 bits. response.PutHex8(return_code); return SendPacketNoLock(response.GetString()); } } static void AppendHexValue(StreamString &response, const uint8_t *buf, uint32_t buf_size, bool swap) { int64_t i; if (swap) { for (i = buf_size - 1; i >= 0; i--) response.PutHex8(buf[i]); } else { for (i = 0; i < buf_size; i++) response.PutHex8(buf[i]); } } static void WriteRegisterValueInHexFixedWidth( StreamString &response, NativeRegisterContextSP ®_ctx_sp, const RegisterInfo ®_info, const RegisterValue *reg_value_p, lldb::ByteOrder byte_order) { RegisterValue reg_value; if (!reg_value_p) { Status error = reg_ctx_sp->ReadRegister(®_info, reg_value); if (error.Success()) reg_value_p = ®_value; // else log. } if (reg_value_p) { AppendHexValue(response, (const uint8_t *)reg_value_p->GetBytes(), reg_value_p->GetByteSize(), byte_order == lldb::eByteOrderLittle); } else { // Zero-out any unreadable values. if (reg_info.byte_size > 0) { std::basic_string zeros(reg_info.byte_size, '\0'); AppendHexValue(response, zeros.data(), zeros.size(), false); } } } static JSONObject::SP GetRegistersAsJSON(NativeThreadProtocol &thread) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); NativeRegisterContextSP reg_ctx_sp = thread.GetRegisterContext(); if (!reg_ctx_sp) return nullptr; JSONObject::SP register_object_sp = std::make_shared(); #ifdef LLDB_JTHREADSINFO_FULL_REGISTER_SET // Expedite all registers in the first register set (i.e. should be GPRs) that // are not contained in other registers. const RegisterSet *reg_set_p = reg_ctx_sp->GetRegisterSet(0); if (!reg_set_p) return nullptr; for (const uint32_t *reg_num_p = reg_set_p->registers; *reg_num_p != LLDB_INVALID_REGNUM; ++reg_num_p) { uint32_t reg_num = *reg_num_p; #else // Expedite only a couple of registers until we figure out why sending // registers is // expensive. static const uint32_t k_expedited_registers[] = { LLDB_REGNUM_GENERIC_PC, LLDB_REGNUM_GENERIC_SP, LLDB_REGNUM_GENERIC_FP, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM}; for (const uint32_t *generic_reg_p = k_expedited_registers; *generic_reg_p != LLDB_INVALID_REGNUM; ++generic_reg_p) { uint32_t reg_num = reg_ctx_sp->ConvertRegisterKindToRegisterNumber( eRegisterKindGeneric, *generic_reg_p); if (reg_num == LLDB_INVALID_REGNUM) continue; // Target does not support the given register. #endif const RegisterInfo *const reg_info_p = reg_ctx_sp->GetRegisterInfoAtIndex(reg_num); if (reg_info_p == nullptr) { if (log) log->Printf( "%s failed to get register info for register index %" PRIu32, __FUNCTION__, reg_num); continue; } if (reg_info_p->value_regs != nullptr) continue; // Only expedite registers that are not contained in other // registers. RegisterValue reg_value; Status error = reg_ctx_sp->ReadRegister(reg_info_p, reg_value); if (error.Fail()) { if (log) log->Printf("%s failed to read register '%s' index %" PRIu32 ": %s", __FUNCTION__, reg_info_p->name ? reg_info_p->name : "", reg_num, error.AsCString()); continue; } StreamString stream; WriteRegisterValueInHexFixedWidth(stream, reg_ctx_sp, *reg_info_p, ®_value, lldb::eByteOrderBig); register_object_sp->SetObject( llvm::to_string(reg_num), std::make_shared(stream.GetString())); } return register_object_sp; } static const char *GetStopReasonString(StopReason stop_reason) { switch (stop_reason) { case eStopReasonTrace: return "trace"; case eStopReasonBreakpoint: return "breakpoint"; case eStopReasonWatchpoint: return "watchpoint"; case eStopReasonSignal: return "signal"; case eStopReasonException: return "exception"; case eStopReasonExec: return "exec"; case eStopReasonInstrumentation: case eStopReasonInvalid: case eStopReasonPlanComplete: case eStopReasonThreadExiting: case eStopReasonNone: break; // ignored } return nullptr; } static JSONArray::SP GetJSONThreadsInfo(NativeProcessProtocol &process, bool abridged) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); JSONArray::SP threads_array_sp = std::make_shared(); // Ensure we can get info on the given thread. uint32_t thread_idx = 0; for (NativeThreadProtocolSP thread_sp; (thread_sp = process.GetThreadAtIndex(thread_idx)) != nullptr; ++thread_idx) { lldb::tid_t tid = thread_sp->GetID(); // Grab the reason this thread stopped. struct ThreadStopInfo tid_stop_info; std::string description; if (!thread_sp->GetStopReason(tid_stop_info, description)) return nullptr; const int signum = tid_stop_info.details.signal.signo; if (log) { log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " got signal signo = %d, reason = %d, exc_type = %" PRIu64, __FUNCTION__, process.GetID(), tid, signum, tid_stop_info.reason, tid_stop_info.details.exception.type); } JSONObject::SP thread_obj_sp = std::make_shared(); threads_array_sp->AppendObject(thread_obj_sp); if (!abridged) { if (JSONObject::SP registers_sp = GetRegistersAsJSON(*thread_sp)) thread_obj_sp->SetObject("registers", registers_sp); } thread_obj_sp->SetObject("tid", std::make_shared(tid)); if (signum != 0) thread_obj_sp->SetObject("signal", std::make_shared(signum)); const std::string thread_name = thread_sp->GetName(); if (!thread_name.empty()) thread_obj_sp->SetObject("name", std::make_shared(thread_name)); if (const char *stop_reason_str = GetStopReasonString(tid_stop_info.reason)) thread_obj_sp->SetObject("reason", std::make_shared(stop_reason_str)); if (!description.empty()) thread_obj_sp->SetObject("description", std::make_shared(description)); if ((tid_stop_info.reason == eStopReasonException) && tid_stop_info.details.exception.type) { thread_obj_sp->SetObject( "metype", std::make_shared(tid_stop_info.details.exception.type)); JSONArray::SP medata_array_sp = std::make_shared(); for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count; ++i) { medata_array_sp->AppendObject(std::make_shared( tid_stop_info.details.exception.data[i])); } thread_obj_sp->SetObject("medata", medata_array_sp); } // TODO: Expedite interesting regions of inferior memory } return threads_array_sp; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread( lldb::tid_t tid) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); // Ensure we have a debugged process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) return SendErrorResponse(50); if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s preparing packet for pid %" PRIu64 " tid %" PRIu64, __FUNCTION__, m_debugged_process_sp->GetID(), tid); // Ensure we can get info on the given thread. NativeThreadProtocolSP thread_sp(m_debugged_process_sp->GetThreadByID(tid)); if (!thread_sp) return SendErrorResponse(51); // Grab the reason this thread stopped. struct ThreadStopInfo tid_stop_info; std::string description; if (!thread_sp->GetStopReason(tid_stop_info, description)) return SendErrorResponse(52); // FIXME implement register handling for exec'd inferiors. // if (tid_stop_info.reason == eStopReasonExec) // { // const bool force = true; // InitializeRegisters(force); // } StreamString response; // Output the T packet with the thread response.PutChar('T'); int signum = tid_stop_info.details.signal.signo; if (log) { log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " got signal signo = %d, reason = %d, exc_type = %" PRIu64, __FUNCTION__, m_debugged_process_sp->GetID(), tid, signum, tid_stop_info.reason, tid_stop_info.details.exception.type); } // Print the signal number. response.PutHex8(signum & 0xff); // Include the tid. response.Printf("thread:%" PRIx64 ";", tid); // Include the thread name if there is one. const std::string thread_name = thread_sp->GetName(); if (!thread_name.empty()) { size_t thread_name_len = thread_name.length(); if (::strcspn(thread_name.c_str(), "$#+-;:") == thread_name_len) { response.PutCString("name:"); response.PutCString(thread_name); } else { // The thread name contains special chars, send as hex bytes. response.PutCString("hexname:"); response.PutCStringAsRawHex8(thread_name.c_str()); } response.PutChar(';'); } // If a 'QListThreadsInStopReply' was sent to enable this feature, we // will send all thread IDs back in the "threads" key whose value is // a list of hex thread IDs separated by commas: // "threads:10a,10b,10c;" // This will save the debugger from having to send a pair of qfThreadInfo // and qsThreadInfo packets, but it also might take a lot of room in the // stop reply packet, so it must be enabled only on systems where there // are no limits on packet lengths. if (m_list_threads_in_stop_reply) { response.PutCString("threads:"); uint32_t thread_index = 0; NativeThreadProtocolSP listed_thread_sp; for (listed_thread_sp = m_debugged_process_sp->GetThreadAtIndex(thread_index); listed_thread_sp; ++thread_index, listed_thread_sp = m_debugged_process_sp->GetThreadAtIndex( thread_index)) { if (thread_index > 0) response.PutChar(','); response.Printf("%" PRIx64, listed_thread_sp->GetID()); } response.PutChar(';'); // Include JSON info that describes the stop reason for any threads // that actually have stop reasons. We use the new "jstopinfo" key // whose values is hex ascii JSON that contains the thread IDs // thread stop info only for threads that have stop reasons. Only send // this if we have more than one thread otherwise this packet has all // the info it needs. if (thread_index > 0) { const bool threads_with_valid_stop_info_only = true; JSONArray::SP threads_info_sp = GetJSONThreadsInfo( *m_debugged_process_sp, threads_with_valid_stop_info_only); if (threads_info_sp) { response.PutCString("jstopinfo:"); StreamString unescaped_response; threads_info_sp->Write(unescaped_response); response.PutCStringAsRawHex8(unescaped_response.GetData()); response.PutChar(';'); } else if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to prepare a " "jstopinfo field for pid %" PRIu64, __FUNCTION__, m_debugged_process_sp->GetID()); } uint32_t i = 0; response.PutCString("thread-pcs"); char delimiter = ':'; for (NativeThreadProtocolSP thread_sp; (thread_sp = m_debugged_process_sp->GetThreadAtIndex(i)) != nullptr; ++i) { NativeRegisterContextSP reg_ctx_sp = thread_sp->GetRegisterContext(); if (!reg_ctx_sp) continue; uint32_t reg_to_read = reg_ctx_sp->ConvertRegisterKindToRegisterNumber( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); const RegisterInfo *const reg_info_p = reg_ctx_sp->GetRegisterInfoAtIndex(reg_to_read); RegisterValue reg_value; Status error = reg_ctx_sp->ReadRegister(reg_info_p, reg_value); if (error.Fail()) { if (log) log->Printf("%s failed to read register '%s' index %" PRIu32 ": %s", __FUNCTION__, reg_info_p->name ? reg_info_p->name : "", reg_to_read, error.AsCString()); continue; } response.PutChar(delimiter); delimiter = ','; WriteRegisterValueInHexFixedWidth(response, reg_ctx_sp, *reg_info_p, ®_value, endian::InlHostByteOrder()); } response.PutChar(';'); } // // Expedite registers. // // Grab the register context. NativeRegisterContextSP reg_ctx_sp = thread_sp->GetRegisterContext(); if (reg_ctx_sp) { // Expedite all registers in the first register set (i.e. should be GPRs) // that are not contained in other registers. const RegisterSet *reg_set_p; if (reg_ctx_sp->GetRegisterSetCount() > 0 && ((reg_set_p = reg_ctx_sp->GetRegisterSet(0)) != nullptr)) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s expediting registers " "from set '%s' (registers set count: %zu)", __FUNCTION__, reg_set_p->name ? reg_set_p->name : "", reg_set_p->num_registers); for (const uint32_t *reg_num_p = reg_set_p->registers; *reg_num_p != LLDB_INVALID_REGNUM; ++reg_num_p) { const RegisterInfo *const reg_info_p = reg_ctx_sp->GetRegisterInfoAtIndex(*reg_num_p); if (reg_info_p == nullptr) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to get " "register info for register set '%s', register index " "%" PRIu32, __FUNCTION__, reg_set_p->name ? reg_set_p->name : "", *reg_num_p); } else if (reg_info_p->value_regs == nullptr) { // Only expediate registers that are not contained in other registers. RegisterValue reg_value; Status error = reg_ctx_sp->ReadRegister(reg_info_p, reg_value); if (error.Success()) { response.Printf("%.02x:", *reg_num_p); WriteRegisterValueInHexFixedWidth(response, reg_ctx_sp, *reg_info_p, ®_value, lldb::eByteOrderBig); response.PutChar(';'); } else { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to read " "register '%s' index %" PRIu32 ": %s", __FUNCTION__, reg_info_p->name ? reg_info_p->name : "", *reg_num_p, error.AsCString()); } } } } } const char *reason_str = GetStopReasonString(tid_stop_info.reason); if (reason_str != nullptr) { response.Printf("reason:%s;", reason_str); } if (!description.empty()) { // Description may contains special chars, send as hex bytes. response.PutCString("description:"); response.PutCStringAsRawHex8(description.c_str()); response.PutChar(';'); } else if ((tid_stop_info.reason == eStopReasonException) && tid_stop_info.details.exception.type) { response.PutCString("metype:"); response.PutHex64(tid_stop_info.details.exception.type); response.PutCString(";mecount:"); response.PutHex32(tid_stop_info.details.exception.data_count); response.PutChar(';'); for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count; ++i) { response.PutCString("medata:"); response.PutHex64(tid_stop_info.details.exception.data[i]); response.PutChar(';'); } } return SendPacketNoLock(response.GetString()); } void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Exited( NativeProcessProtocol *process) { assert(process && "process cannot be NULL"); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); PacketResult result = SendStopReasonForState(StateType::eStateExited); if (result != PacketResult::Success) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to send stop " "notification for PID %" PRIu64 ", state: eStateExited", __FUNCTION__, process->GetID()); } // Close the pipe to the inferior terminal i/o if we launched it // and set one up. MaybeCloseInferiorTerminalConnection(); // We are ready to exit the debug monitor. m_exit_now = true; } void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Stopped( NativeProcessProtocol *process) { assert(process && "process cannot be NULL"); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); // Send the stop reason unless this is the stop after the // launch or attach. switch (m_inferior_prev_state) { case eStateLaunching: case eStateAttaching: // Don't send anything per debugserver behavior. break; default: // In all other cases, send the stop reason. PacketResult result = SendStopReasonForState(StateType::eStateStopped); if (result != PacketResult::Success) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to send stop " "notification for PID %" PRIu64 ", state: eStateExited", __FUNCTION__, process->GetID()); } break; } } void GDBRemoteCommunicationServerLLGS::ProcessStateChanged( NativeProcessProtocol *process, lldb::StateType state) { assert(process && "process cannot be NULL"); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) { log->Printf("GDBRemoteCommunicationServerLLGS::%s called with " "NativeProcessProtocol pid %" PRIu64 ", state: %s", __FUNCTION__, process->GetID(), StateAsCString(state)); } switch (state) { case StateType::eStateRunning: StartSTDIOForwarding(); break; case StateType::eStateStopped: // Make sure we get all of the pending stdout/stderr from the inferior // and send it to the lldb host before we send the state change // notification SendProcessOutput(); // Then stop the forwarding, so that any late output (see llvm.org/pr25652) // does not // interfere with our protocol. StopSTDIOForwarding(); HandleInferiorState_Stopped(process); break; case StateType::eStateExited: // Same as above SendProcessOutput(); StopSTDIOForwarding(); HandleInferiorState_Exited(process); break; default: if (log) { log->Printf("GDBRemoteCommunicationServerLLGS::%s didn't handle state " "change for pid %" PRIu64 ", new state: %s", __FUNCTION__, process->GetID(), StateAsCString(state)); } break; } // Remember the previous state reported to us. m_inferior_prev_state = state; } void GDBRemoteCommunicationServerLLGS::DidExec(NativeProcessProtocol *process) { ClearProcessSpecificData(); } void GDBRemoteCommunicationServerLLGS::DataAvailableCallback() { Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_COMM)); if (!m_handshake_completed) { if (!HandshakeWithClient()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s handshake with " "client failed, exiting", __FUNCTION__); m_mainloop.RequestTermination(); return; } m_handshake_completed = true; } bool interrupt = false; bool done = false; Status error; while (true) { const PacketResult result = GetPacketAndSendResponse( std::chrono::microseconds(0), error, interrupt, done); if (result == PacketResult::ErrorReplyTimeout) break; // No more packets in the queue if ((result != PacketResult::Success)) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s processing a packet " "failed: %s", __FUNCTION__, error.AsCString()); m_mainloop.RequestTermination(); break; } } } Status GDBRemoteCommunicationServerLLGS::InitializeConnection( std::unique_ptr &&connection) { IOObjectSP read_object_sp = connection->GetReadObject(); GDBRemoteCommunicationServer::SetConnection(connection.release()); Status error; m_network_handle_up = m_mainloop.RegisterReadObject( read_object_sp, [this](MainLoopBase &) { DataAvailableCallback(); }, error); return error; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::SendONotification(const char *buffer, uint32_t len) { if ((buffer == nullptr) || (len == 0)) { // Nothing to send. return PacketResult::Success; } StreamString response; response.PutChar('O'); response.PutBytesAsRawHex8(buffer, len); return SendPacketNoLock(response.GetString()); } Status GDBRemoteCommunicationServerLLGS::SetSTDIOFileDescriptor(int fd) { Status error; // Set up the reading/handling of process I/O std::unique_ptr conn_up( new ConnectionFileDescriptor(fd, true)); if (!conn_up) { error.SetErrorString("failed to create ConnectionFileDescriptor"); return error; } m_stdio_communication.SetCloseOnEOF(false); m_stdio_communication.SetConnection(conn_up.release()); if (!m_stdio_communication.IsConnected()) { error.SetErrorString( "failed to set connection for inferior I/O communication"); return error; } return Status(); } void GDBRemoteCommunicationServerLLGS::StartSTDIOForwarding() { // Don't forward if not connected (e.g. when attaching). if (!m_stdio_communication.IsConnected()) return; Status error; lldbassert(!m_stdio_handle_up); m_stdio_handle_up = m_mainloop.RegisterReadObject( m_stdio_communication.GetConnection()->GetReadObject(), [this](MainLoopBase &) { SendProcessOutput(); }, error); if (!m_stdio_handle_up) { // Not much we can do about the failure. Log it and continue without // forwarding. if (Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)) log->Printf("GDBRemoteCommunicationServerLLGS::%s Failed to set up stdio " "forwarding: %s", __FUNCTION__, error.AsCString()); } } void GDBRemoteCommunicationServerLLGS::StopSTDIOForwarding() { m_stdio_handle_up.reset(); } void GDBRemoteCommunicationServerLLGS::SendProcessOutput() { char buffer[1024]; ConnectionStatus status; Status error; while (true) { size_t bytes_read = m_stdio_communication.Read( buffer, sizeof buffer, std::chrono::microseconds(0), status, &error); switch (status) { case eConnectionStatusSuccess: SendONotification(buffer, bytes_read); break; case eConnectionStatusLostConnection: case eConnectionStatusEndOfFile: case eConnectionStatusError: case eConnectionStatusNoConnection: if (Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)) log->Printf("GDBRemoteCommunicationServerLLGS::%s Stopping stdio " "forwarding as communication returned status %d (error: " "%s)", __FUNCTION__, status, error.AsCString()); m_stdio_handle_up.reset(); return; case eConnectionStatusInterrupted: case eConnectionStatusTimedOut: return; } } +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jTraceStart( + StringExtractorGDBRemote &packet) { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + // Fail if we don't have a current process. + if (!m_debugged_process_sp || + (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(68); + + if (!packet.ConsumeFront("jTraceStart:")) + return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet "); + + TraceOptions options; + uint64_t type = std::numeric_limits::max(); + uint64_t buffersize = std::numeric_limits::max(); + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + uint64_t metabuffersize = std::numeric_limits::max(); + + auto json_object = StructuredData::ParseJSON(packet.Peek()); + + if (!json_object || + json_object->GetType() != lldb::eStructuredDataTypeDictionary) + return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet "); + + auto json_dict = json_object->GetAsDictionary(); + + json_dict->GetValueForKeyAsInteger("metabuffersize", metabuffersize); + options.setMetaDataBufferSize(metabuffersize); + + json_dict->GetValueForKeyAsInteger("buffersize", buffersize); + options.setTraceBufferSize(buffersize); + + json_dict->GetValueForKeyAsInteger("type", type); + options.setType(static_cast(type)); + + json_dict->GetValueForKeyAsInteger("threadid", tid); + options.setThreadID(tid); + + StructuredData::ObjectSP custom_params_sp = + json_dict->GetValueForKey("params"); + if (custom_params_sp && + custom_params_sp->GetType() != lldb::eStructuredDataTypeDictionary) + return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet "); + + options.setTraceParams( + static_pointer_cast(custom_params_sp)); + + if (buffersize == std::numeric_limits::max() || + type != lldb::TraceType::eTraceTypeProcessorTrace) { + LLDB_LOG(log, "Ill formed packet buffersize = {0} type = {1}", buffersize, + type); + return SendIllFormedResponse(packet, "JTrace:start: Ill formed packet "); + } + + Status error; + lldb::user_id_t uid = LLDB_INVALID_UID; + uid = m_debugged_process_sp->StartTrace(options, error); + LLDB_LOG(log, "uid is {0} , error is {1}", uid, error.GetError()); + if (error.Fail()) + return SendErrorResponse(error.GetError()); + + StreamGDBRemote response; + response.Printf("%" PRIx64, uid); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jTraceStop( + StringExtractorGDBRemote &packet) { + // Fail if we don't have a current process. + if (!m_debugged_process_sp || + (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(68); + + if (!packet.ConsumeFront("jTraceStop:")) + return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet "); + + lldb::user_id_t uid = LLDB_INVALID_UID; + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + + auto json_object = StructuredData::ParseJSON(packet.Peek()); + + if (!json_object || + json_object->GetType() != lldb::eStructuredDataTypeDictionary) + return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet "); + + auto json_dict = json_object->GetAsDictionary(); + + if (!json_dict->GetValueForKeyAsInteger("traceid", uid)) + return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet "); + + json_dict->GetValueForKeyAsInteger("threadid", tid); + + Status error = m_debugged_process_sp->StopTrace(uid, tid); + + if (error.Fail()) + return SendErrorResponse(error.GetError()); + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jTraceConfigRead( + StringExtractorGDBRemote &packet) { + + // Fail if we don't have a current process. + if (!m_debugged_process_sp || + (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(68); + + if (!packet.ConsumeFront("jTraceConfigRead:")) + return SendIllFormedResponse(packet, + "jTraceConfigRead: Ill formed packet "); + + lldb::user_id_t uid = LLDB_INVALID_UID; + lldb::tid_t threadid = LLDB_INVALID_THREAD_ID; + + auto json_object = StructuredData::ParseJSON(packet.Peek()); + + if (!json_object || + json_object->GetType() != lldb::eStructuredDataTypeDictionary) + return SendIllFormedResponse(packet, + "jTraceConfigRead: Ill formed packet "); + + auto json_dict = json_object->GetAsDictionary(); + + if (!json_dict->GetValueForKeyAsInteger("traceid", uid)) + return SendIllFormedResponse(packet, + "jTraceConfigRead: Ill formed packet "); + + json_dict->GetValueForKeyAsInteger("threadid", threadid); + + TraceOptions options; + StreamGDBRemote response; + + options.setThreadID(threadid); + Status error = m_debugged_process_sp->GetTraceConfig(uid, options); + + if (error.Fail()) + return SendErrorResponse(error.GetError()); + + StreamGDBRemote escaped_response; + StructuredData::Dictionary json_packet; + + json_packet.AddIntegerItem("type", options.getType()); + json_packet.AddIntegerItem("buffersize", options.getTraceBufferSize()); + json_packet.AddIntegerItem("metabuffersize", options.getMetaDataBufferSize()); + + StructuredData::DictionarySP custom_params = options.getTraceParams(); + if (custom_params) + json_packet.AddItem("params", custom_params); + + StreamString json_string; + json_packet.Dump(json_string, false); + escaped_response.PutEscapedBytes(json_string.GetData(), + json_string.GetSize()); + return SendPacketNoLock(escaped_response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jTraceRead( + StringExtractorGDBRemote &packet) { + + // Fail if we don't have a current process. + if (!m_debugged_process_sp || + (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(68); + + enum PacketType { MetaData, BufferData }; + PacketType tracetype = MetaData; + + if (packet.ConsumeFront("jTraceBufferRead:")) + tracetype = BufferData; + else if (packet.ConsumeFront("jTraceMetaRead:")) + tracetype = MetaData; + else { + return SendIllFormedResponse(packet, "jTrace: Ill formed packet "); + } + + lldb::user_id_t uid = LLDB_INVALID_UID; + + size_t byte_count = std::numeric_limits::max(); + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + size_t offset = std::numeric_limits::max(); + + auto json_object = StructuredData::ParseJSON(packet.Peek()); + + if (!json_object || + json_object->GetType() != lldb::eStructuredDataTypeDictionary) + return SendIllFormedResponse(packet, "jTrace: Ill formed packet "); + + auto json_dict = json_object->GetAsDictionary(); + + if (!json_dict->GetValueForKeyAsInteger("traceid", uid) || + !json_dict->GetValueForKeyAsInteger("offset", offset) || + !json_dict->GetValueForKeyAsInteger("buffersize", byte_count)) + return SendIllFormedResponse(packet, "jTrace: Ill formed packet "); + + json_dict->GetValueForKeyAsInteger("threadid", tid); + + // Allocate the response buffer. + std::unique_ptr buffer (new (std::nothrow) uint8_t[byte_count]); + if (!buffer) + return SendErrorResponse(0x78); + + StreamGDBRemote response; + Status error; + llvm::MutableArrayRef buf(buffer.get(), byte_count); + + if (tracetype == BufferData) + error = m_debugged_process_sp->GetData(uid, tid, buf, offset); + else if (tracetype == MetaData) + error = m_debugged_process_sp->GetMetaData(uid, tid, buf, offset); + + if (error.Fail()) + return SendErrorResponse(error.GetError()); + + for (size_t i = 0; i < buf.size(); ++i) + response.PutHex8(buf[i]); + + StreamGDBRemote escaped_response; + escaped_response.PutEscapedBytes(response.GetData(), response.GetSize()); + return SendPacketNoLock(escaped_response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo( StringExtractorGDBRemote &packet) { // Fail if we don't have a current process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) return SendErrorResponse(68); lldb::pid_t pid = m_debugged_process_sp->GetID(); if (pid == LLDB_INVALID_PROCESS_ID) return SendErrorResponse(1); ProcessInstanceInfo proc_info; if (!Host::GetProcessInfo(pid, proc_info)) return SendErrorResponse(1); StreamString response; CreateProcessInfoResponse_DebugServerStyle(proc_info, response); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qC(StringExtractorGDBRemote &packet) { // Fail if we don't have a current process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) return SendErrorResponse(68); // Make sure we set the current thread so g and p packets return // the data the gdb will expect. lldb::tid_t tid = m_debugged_process_sp->GetCurrentThreadID(); SetCurrentThreadID(tid); NativeThreadProtocolSP thread_sp = m_debugged_process_sp->GetCurrentThread(); if (!thread_sp) return SendErrorResponse(69); StreamString response; response.Printf("QC%" PRIx64, thread_sp->GetID()); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_k(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); StopSTDIOForwarding(); if (!m_debugged_process_sp) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s No debugged process found.", __FUNCTION__); return PacketResult::Success; } Status error = m_debugged_process_sp->Kill(); if (error.Fail() && log) log->Printf("GDBRemoteCommunicationServerLLGS::%s Failed to kill debugged " "process %" PRIu64 ": %s", __FUNCTION__, m_debugged_process_sp->GetID(), error.AsCString()); // No OK response for kill packet. // return SendOKResponse (); return PacketResult::Success; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_QSetDisableASLR( StringExtractorGDBRemote &packet) { packet.SetFilePos(::strlen("QSetDisableASLR:")); if (packet.GetU32(0)) m_process_launch_info.GetFlags().Set(eLaunchFlagDisableASLR); else m_process_launch_info.GetFlags().Clear(eLaunchFlagDisableASLR); return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_QSetWorkingDir( StringExtractorGDBRemote &packet) { packet.SetFilePos(::strlen("QSetWorkingDir:")); std::string path; packet.GetHexByteString(path); m_process_launch_info.SetWorkingDirectory(FileSpec{path, true}); return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qGetWorkingDir( StringExtractorGDBRemote &packet) { FileSpec working_dir{m_process_launch_info.GetWorkingDirectory()}; if (working_dir) { StreamString response; response.PutCStringAsRawHex8(working_dir.GetCString()); return SendPacketNoLock(response.GetString()); } return SendErrorResponse(14); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_C(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); // Ensure we have a native process. if (!m_debugged_process_sp) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s no debugged process " "shared pointer", __FUNCTION__); return SendErrorResponse(0x36); } // Pull out the signal number. packet.SetFilePos(::strlen("C")); if (packet.GetBytesLeft() < 1) { // Shouldn't be using a C without a signal. return SendIllFormedResponse(packet, "C packet specified without signal."); } const uint32_t signo = packet.GetHexMaxU32(false, std::numeric_limits::max()); if (signo == std::numeric_limits::max()) return SendIllFormedResponse(packet, "failed to parse signal number"); // Handle optional continue address. if (packet.GetBytesLeft() > 0) { // FIXME add continue at address support for $C{signo}[;{continue-address}]. if (*packet.Peek() == ';') return SendUnimplementedResponse(packet.GetStringRef().c_str()); else return SendIllFormedResponse( packet, "unexpected content after $C{signal-number}"); } ResumeActionList resume_actions(StateType::eStateRunning, 0); Status error; // We have two branches: what to do if a continue thread is specified (in // which case we target // sending the signal to that thread), or when we don't have a continue thread // set (in which // case we send a signal to the process). // TODO discuss with Greg Clayton, make sure this makes sense. lldb::tid_t signal_tid = GetContinueThreadID(); if (signal_tid != LLDB_INVALID_THREAD_ID) { // The resume action for the continue thread (or all threads if a continue // thread is not set). ResumeAction action = {GetContinueThreadID(), StateType::eStateRunning, static_cast(signo)}; // Add the action for the continue thread (or all threads when the continue // thread isn't present). resume_actions.Append(action); } else { // Send the signal to the process since we weren't targeting a specific // continue thread with the signal. error = m_debugged_process_sp->Signal(signo); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to send " "signal for process %" PRIu64 ": %s", __FUNCTION__, m_debugged_process_sp->GetID(), error.AsCString()); return SendErrorResponse(0x52); } } // Resume the threads. error = m_debugged_process_sp->Resume(resume_actions); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to resume " "threads for process %" PRIu64 ": %s", __FUNCTION__, m_debugged_process_sp->GetID(), error.AsCString()); return SendErrorResponse(0x38); } // Don't send an "OK" packet; response is the stopped/exited message. return PacketResult::Success; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_c(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); packet.SetFilePos(packet.GetFilePos() + ::strlen("c")); // For now just support all continue. const bool has_continue_address = (packet.GetBytesLeft() > 0); if (has_continue_address) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s not implemented for " "c{address} variant [%s remains]", __FUNCTION__, packet.Peek()); return SendUnimplementedResponse(packet.GetStringRef().c_str()); } // Ensure we have a native process. if (!m_debugged_process_sp) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s no debugged process " "shared pointer", __FUNCTION__); return SendErrorResponse(0x36); } // Build the ResumeActionList ResumeActionList actions(StateType::eStateRunning, 0); Status error = m_debugged_process_sp->Resume(actions); if (error.Fail()) { if (log) { log->Printf( "GDBRemoteCommunicationServerLLGS::%s c failed for process %" PRIu64 ": %s", __FUNCTION__, m_debugged_process_sp->GetID(), error.AsCString()); } return SendErrorResponse(GDBRemoteServerError::eErrorResume); } if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s continued process %" PRIu64, __FUNCTION__, m_debugged_process_sp->GetID()); // No response required from continue. return PacketResult::Success; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_vCont_actions( StringExtractorGDBRemote &packet) { StreamString response; response.Printf("vCont;c;C;s;S"); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_vCont( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s handling vCont packet", __FUNCTION__); packet.SetFilePos(::strlen("vCont")); if (packet.GetBytesLeft() == 0) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s missing action from " "vCont package", __FUNCTION__); return SendIllFormedResponse(packet, "Missing action from vCont package"); } // Check if this is all continue (no options or ";c"). if (::strcmp(packet.Peek(), ";c") == 0) { // Move past the ';', then do a simple 'c'. packet.SetFilePos(packet.GetFilePos() + 1); return Handle_c(packet); } else if (::strcmp(packet.Peek(), ";s") == 0) { // Move past the ';', then do a simple 's'. packet.SetFilePos(packet.GetFilePos() + 1); return Handle_s(packet); } // Ensure we have a native process. if (!m_debugged_process_sp) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s no debugged process " "shared pointer", __FUNCTION__); return SendErrorResponse(0x36); } ResumeActionList thread_actions; while (packet.GetBytesLeft() && *packet.Peek() == ';') { // Skip the semi-colon. packet.GetChar(); // Build up the thread action. ResumeAction thread_action; thread_action.tid = LLDB_INVALID_THREAD_ID; thread_action.state = eStateInvalid; thread_action.signal = 0; const char action = packet.GetChar(); switch (action) { case 'C': thread_action.signal = packet.GetHexMaxU32(false, 0); if (thread_action.signal == 0) return SendIllFormedResponse( packet, "Could not parse signal in vCont packet C action"); LLVM_FALLTHROUGH; case 'c': // Continue thread_action.state = eStateRunning; break; case 'S': thread_action.signal = packet.GetHexMaxU32(false, 0); if (thread_action.signal == 0) return SendIllFormedResponse( packet, "Could not parse signal in vCont packet S action"); LLVM_FALLTHROUGH; case 's': // Step thread_action.state = eStateStepping; break; default: return SendIllFormedResponse(packet, "Unsupported vCont action"); break; } // Parse out optional :{thread-id} value. if (packet.GetBytesLeft() && (*packet.Peek() == ':')) { // Consume the separator. packet.GetChar(); thread_action.tid = packet.GetHexMaxU32(false, LLDB_INVALID_THREAD_ID); if (thread_action.tid == LLDB_INVALID_THREAD_ID) return SendIllFormedResponse( packet, "Could not parse thread number in vCont packet"); } thread_actions.Append(thread_action); } Status error = m_debugged_process_sp->Resume(thread_actions); if (error.Fail()) { if (log) { log->Printf("GDBRemoteCommunicationServerLLGS::%s vCont failed for " "process %" PRIu64 ": %s", __FUNCTION__, m_debugged_process_sp->GetID(), error.AsCString()); } return SendErrorResponse(GDBRemoteServerError::eErrorResume); } if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s continued process %" PRIu64, __FUNCTION__, m_debugged_process_sp->GetID()); // No response required from vCont. return PacketResult::Success; } void GDBRemoteCommunicationServerLLGS::SetCurrentThreadID(lldb::tid_t tid) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s setting current thread " "id to %" PRIu64, __FUNCTION__, tid); m_current_tid = tid; if (m_debugged_process_sp) m_debugged_process_sp->SetCurrentThreadID(m_current_tid); } void GDBRemoteCommunicationServerLLGS::SetContinueThreadID(lldb::tid_t tid) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s setting continue thread " "id to %" PRIu64, __FUNCTION__, tid); m_continue_tid = tid; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_stop_reason( StringExtractorGDBRemote &packet) { // Handle the $? gdbremote command. // If no process, indicate error if (!m_debugged_process_sp) return SendErrorResponse(02); return SendStopReasonForState(m_debugged_process_sp->GetState()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::SendStopReasonForState( lldb::StateType process_state) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); switch (process_state) { case eStateAttaching: case eStateLaunching: case eStateRunning: case eStateStepping: case eStateDetached: // NOTE: gdb protocol doc looks like it should return $OK // when everything is running (i.e. no stopped result). return PacketResult::Success; // Ignore case eStateSuspended: case eStateStopped: case eStateCrashed: { lldb::tid_t tid = m_debugged_process_sp->GetCurrentThreadID(); // Make sure we set the current thread so g and p packets return // the data the gdb will expect. SetCurrentThreadID(tid); return SendStopReplyPacketForThread(tid); } case eStateInvalid: case eStateUnloaded: case eStateExited: return SendWResponse(m_debugged_process_sp.get()); default: if (log) { log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 ", current state reporting not handled: %s", __FUNCTION__, m_debugged_process_sp->GetID(), StateAsCString(process_state)); } break; } return SendErrorResponse(0); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qRegisterInfo( StringExtractorGDBRemote &packet) { // Fail if we don't have a current process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) return SendErrorResponse(68); // Ensure we have a thread. NativeThreadProtocolSP thread_sp(m_debugged_process_sp->GetThreadAtIndex(0)); if (!thread_sp) return SendErrorResponse(69); // Get the register context for the first thread. NativeRegisterContextSP reg_context_sp(thread_sp->GetRegisterContext()); if (!reg_context_sp) return SendErrorResponse(69); // Parse out the register number from the request. packet.SetFilePos(strlen("qRegisterInfo")); const uint32_t reg_index = packet.GetHexMaxU32(false, std::numeric_limits::max()); if (reg_index == std::numeric_limits::max()) return SendErrorResponse(69); // Return the end of registers response if we've iterated one past the end of // the register set. if (reg_index >= reg_context_sp->GetUserRegisterCount()) return SendErrorResponse(69); const RegisterInfo *reg_info = reg_context_sp->GetRegisterInfoAtIndex(reg_index); if (!reg_info) return SendErrorResponse(69); // Build the reginfos response. StreamGDBRemote response; response.PutCString("name:"); response.PutCString(reg_info->name); response.PutChar(';'); if (reg_info->alt_name && reg_info->alt_name[0]) { response.PutCString("alt-name:"); response.PutCString(reg_info->alt_name); response.PutChar(';'); } response.Printf("bitsize:%" PRIu32 ";offset:%" PRIu32 ";", reg_info->byte_size * 8, reg_info->byte_offset); switch (reg_info->encoding) { case eEncodingUint: response.PutCString("encoding:uint;"); break; case eEncodingSint: response.PutCString("encoding:sint;"); break; case eEncodingIEEE754: response.PutCString("encoding:ieee754;"); break; case eEncodingVector: response.PutCString("encoding:vector;"); break; default: break; } switch (reg_info->format) { case eFormatBinary: response.PutCString("format:binary;"); break; case eFormatDecimal: response.PutCString("format:decimal;"); break; case eFormatHex: response.PutCString("format:hex;"); break; case eFormatFloat: response.PutCString("format:float;"); break; case eFormatVectorOfSInt8: response.PutCString("format:vector-sint8;"); break; case eFormatVectorOfUInt8: response.PutCString("format:vector-uint8;"); break; case eFormatVectorOfSInt16: response.PutCString("format:vector-sint16;"); break; case eFormatVectorOfUInt16: response.PutCString("format:vector-uint16;"); break; case eFormatVectorOfSInt32: response.PutCString("format:vector-sint32;"); break; case eFormatVectorOfUInt32: response.PutCString("format:vector-uint32;"); break; case eFormatVectorOfFloat32: response.PutCString("format:vector-float32;"); break; case eFormatVectorOfUInt64: response.PutCString("format:vector-uint64;"); break; case eFormatVectorOfUInt128: response.PutCString("format:vector-uint128;"); break; default: break; }; const char *const register_set_name = reg_context_sp->GetRegisterSetNameForRegisterAtIndex(reg_index); if (register_set_name) { response.PutCString("set:"); response.PutCString(register_set_name); response.PutChar(';'); } if (reg_info->kinds[RegisterKind::eRegisterKindEHFrame] != LLDB_INVALID_REGNUM) response.Printf("ehframe:%" PRIu32 ";", reg_info->kinds[RegisterKind::eRegisterKindEHFrame]); if (reg_info->kinds[RegisterKind::eRegisterKindDWARF] != LLDB_INVALID_REGNUM) response.Printf("dwarf:%" PRIu32 ";", reg_info->kinds[RegisterKind::eRegisterKindDWARF]); switch (reg_info->kinds[RegisterKind::eRegisterKindGeneric]) { case LLDB_REGNUM_GENERIC_PC: response.PutCString("generic:pc;"); break; case LLDB_REGNUM_GENERIC_SP: response.PutCString("generic:sp;"); break; case LLDB_REGNUM_GENERIC_FP: response.PutCString("generic:fp;"); break; case LLDB_REGNUM_GENERIC_RA: response.PutCString("generic:ra;"); break; case LLDB_REGNUM_GENERIC_FLAGS: response.PutCString("generic:flags;"); break; case LLDB_REGNUM_GENERIC_ARG1: response.PutCString("generic:arg1;"); break; case LLDB_REGNUM_GENERIC_ARG2: response.PutCString("generic:arg2;"); break; case LLDB_REGNUM_GENERIC_ARG3: response.PutCString("generic:arg3;"); break; case LLDB_REGNUM_GENERIC_ARG4: response.PutCString("generic:arg4;"); break; case LLDB_REGNUM_GENERIC_ARG5: response.PutCString("generic:arg5;"); break; case LLDB_REGNUM_GENERIC_ARG6: response.PutCString("generic:arg6;"); break; case LLDB_REGNUM_GENERIC_ARG7: response.PutCString("generic:arg7;"); break; case LLDB_REGNUM_GENERIC_ARG8: response.PutCString("generic:arg8;"); break; default: break; } if (reg_info->value_regs && reg_info->value_regs[0] != LLDB_INVALID_REGNUM) { response.PutCString("container-regs:"); int i = 0; for (const uint32_t *reg_num = reg_info->value_regs; *reg_num != LLDB_INVALID_REGNUM; ++reg_num, ++i) { if (i > 0) response.PutChar(','); response.Printf("%" PRIx32, *reg_num); } response.PutChar(';'); } if (reg_info->invalidate_regs && reg_info->invalidate_regs[0]) { response.PutCString("invalidate-regs:"); int i = 0; for (const uint32_t *reg_num = reg_info->invalidate_regs; *reg_num != LLDB_INVALID_REGNUM; ++reg_num, ++i) { if (i > 0) response.PutChar(','); response.Printf("%" PRIx32, *reg_num); } response.PutChar(';'); } if (reg_info->dynamic_size_dwarf_expr_bytes) { const size_t dwarf_opcode_len = reg_info->dynamic_size_dwarf_len; response.PutCString("dynamic_size_dwarf_expr_bytes:"); for (uint32_t i = 0; i < dwarf_opcode_len; ++i) response.PutHex8(reg_info->dynamic_size_dwarf_expr_bytes[i]); response.PutChar(';'); } return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); // Fail if we don't have a current process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s() no process (%s), " "returning OK", __FUNCTION__, m_debugged_process_sp ? "invalid process id" : "null m_debugged_process_sp"); return SendOKResponse(); } StreamGDBRemote response; response.PutChar('m'); if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s() starting thread iteration", __FUNCTION__); NativeThreadProtocolSP thread_sp; uint32_t thread_index; for (thread_index = 0, thread_sp = m_debugged_process_sp->GetThreadAtIndex(thread_index); thread_sp; ++thread_index, thread_sp = m_debugged_process_sp->GetThreadAtIndex(thread_index)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s() iterated thread %" PRIu32 "(%s, tid=0x%" PRIx64 ")", __FUNCTION__, thread_index, thread_sp ? "is not null" : "null", thread_sp ? thread_sp->GetID() : LLDB_INVALID_THREAD_ID); if (thread_index > 0) response.PutChar(','); response.Printf("%" PRIx64, thread_sp->GetID()); } if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s() finished thread iteration", __FUNCTION__); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo( StringExtractorGDBRemote &packet) { // FIXME for now we return the full thread list in the initial packet and // always do nothing here. return SendPacketNoLock("l"); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_p(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); // Parse out the register number from the request. packet.SetFilePos(strlen("p")); const uint32_t reg_index = packet.GetHexMaxU32(false, std::numeric_limits::max()); if (reg_index == std::numeric_limits::max()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, could not " "parse register number from request \"%s\"", __FUNCTION__, packet.GetStringRef().c_str()); return SendErrorResponse(0x15); } // Get the thread to use. NativeThreadProtocolSP thread_sp = GetThreadFromSuffix(packet); if (!thread_sp) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no thread available", __FUNCTION__); return SendErrorResponse(0x15); } // Get the thread's register context. NativeRegisterContextSP reg_context_sp(thread_sp->GetRegisterContext()); if (!reg_context_sp) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " failed, no register context available for the thread", __FUNCTION__, m_debugged_process_sp->GetID(), thread_sp->GetID()); return SendErrorResponse(0x15); } // Return the end of registers response if we've iterated one past the end of // the register set. if (reg_index >= reg_context_sp->GetUserRegisterCount()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested " "register %" PRIu32 " beyond register count %" PRIu32, __FUNCTION__, reg_index, reg_context_sp->GetUserRegisterCount()); return SendErrorResponse(0x15); } const RegisterInfo *reg_info = reg_context_sp->GetRegisterInfoAtIndex(reg_index); if (!reg_info) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested " "register %" PRIu32 " returned NULL", __FUNCTION__, reg_index); return SendErrorResponse(0x15); } // Build the reginfos response. StreamGDBRemote response; // Retrieve the value RegisterValue reg_value; Status error = reg_context_sp->ReadRegister(reg_info, reg_value); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, read of " "requested register %" PRIu32 " (%s) failed: %s", __FUNCTION__, reg_index, reg_info->name, error.AsCString()); return SendErrorResponse(0x15); } const uint8_t *const data = reinterpret_cast(reg_value.GetBytes()); if (!data) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to get data " "bytes from requested register %" PRIu32, __FUNCTION__, reg_index); return SendErrorResponse(0x15); } // FIXME flip as needed to get data in big/little endian format for this host. for (uint32_t i = 0; i < reg_value.GetByteSize(); ++i) response.PutHex8(data[i]); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_P(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); // Ensure there is more content. if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Empty P packet"); // Parse out the register number from the request. packet.SetFilePos(strlen("P")); const uint32_t reg_index = packet.GetHexMaxU32(false, std::numeric_limits::max()); if (reg_index == std::numeric_limits::max()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, could not " "parse register number from request \"%s\"", __FUNCTION__, packet.GetStringRef().c_str()); return SendErrorResponse(0x29); } // Note debugserver would send an E30 here. if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != '=')) return SendIllFormedResponse( packet, "P packet missing '=' char after register number"); // Get process architecture. ArchSpec process_arch; if (!m_debugged_process_sp || !m_debugged_process_sp->GetArchitecture(process_arch)) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to retrieve " "inferior architecture", __FUNCTION__); return SendErrorResponse(0x49); } // Parse out the value. uint8_t reg_bytes[32]; // big enough to support up to 256 bit ymmN register size_t reg_size = packet.GetHexBytesAvail(reg_bytes); // Get the thread to use. NativeThreadProtocolSP thread_sp = GetThreadFromSuffix(packet); if (!thread_sp) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, no thread " "available (thread index 0)", __FUNCTION__); return SendErrorResponse(0x28); } // Get the thread's register context. NativeRegisterContextSP reg_context_sp(thread_sp->GetRegisterContext()); if (!reg_context_sp) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " failed, no register context available for the thread", __FUNCTION__, m_debugged_process_sp->GetID(), thread_sp->GetID()); return SendErrorResponse(0x15); } const RegisterInfo *reg_info = reg_context_sp->GetRegisterInfoAtIndex(reg_index); if (!reg_info) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested " "register %" PRIu32 " returned NULL", __FUNCTION__, reg_index); return SendErrorResponse(0x48); } // Return the end of registers response if we've iterated one past the end of // the register set. if (reg_index >= reg_context_sp->GetUserRegisterCount()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested " "register %" PRIu32 " beyond register count %" PRIu32, __FUNCTION__, reg_index, reg_context_sp->GetUserRegisterCount()); return SendErrorResponse(0x47); } // The dwarf expression are evaluate on host site // which may cause register size to change // Hence the reg_size may not be same as reg_info->bytes_size if ((reg_size != reg_info->byte_size) && !(reg_info->dynamic_size_dwarf_expr_bytes)) { return SendIllFormedResponse(packet, "P packet register size is incorrect"); } // Build the reginfos response. StreamGDBRemote response; RegisterValue reg_value(reg_bytes, reg_size, process_arch.GetByteOrder()); Status error = reg_context_sp->WriteRegister(reg_info, reg_value); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, write of " "requested register %" PRIu32 " (%s) failed: %s", __FUNCTION__, reg_index, reg_info->name, error.AsCString()); return SendErrorResponse(0x32); } return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_H(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); // Fail if we don't have a current process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } // Parse out which variant of $H is requested. packet.SetFilePos(strlen("H")); if (packet.GetBytesLeft() < 1) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, H command " "missing {g,c} variant", __FUNCTION__); return SendIllFormedResponse(packet, "H command missing {g,c} variant"); } const char h_variant = packet.GetChar(); switch (h_variant) { case 'g': break; case 'c': break; default: if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, invalid $H variant %c", __FUNCTION__, h_variant); return SendIllFormedResponse(packet, "H variant unsupported, should be c or g"); } // Parse out the thread number. // FIXME return a parse success/fail value. All values are valid here. const lldb::tid_t tid = packet.GetHexMaxU64(false, std::numeric_limits::max()); // Ensure we have the given thread when not specifying -1 (all threads) or 0 // (any thread). if (tid != LLDB_INVALID_THREAD_ID && tid != 0) { NativeThreadProtocolSP thread_sp(m_debugged_process_sp->GetThreadByID(tid)); if (!thread_sp) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, tid %" PRIu64 " not found", __FUNCTION__, tid); return SendErrorResponse(0x15); } } // Now switch the given thread type. switch (h_variant) { case 'g': SetCurrentThreadID(tid); break; case 'c': SetContinueThreadID(tid); break; default: assert(false && "unsupported $H variant - shouldn't get here"); return SendIllFormedResponse(packet, "H variant unsupported, should be c or g"); } return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_I(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); // Fail if we don't have a current process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } packet.SetFilePos(::strlen("I")); uint8_t tmp[4096]; for (;;) { size_t read = packet.GetHexBytesAvail(tmp); if (read == 0) { break; } // write directly to stdin *this might block if stdin buffer is full* // TODO: enqueue this block in circular buffer and send window size to // remote host ConnectionStatus status; Status error; m_stdio_communication.Write(tmp, read, status, &error); if (error.Fail()) { return SendErrorResponse(0x15); } } return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_interrupt( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); // Fail if we don't have a current process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } // Interrupt the process. Status error = m_debugged_process_sp->Interrupt(); if (error.Fail()) { if (log) { log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed for process %" PRIu64 ": %s", __FUNCTION__, m_debugged_process_sp->GetID(), error.AsCString()); } return SendErrorResponse(GDBRemoteServerError::eErrorResume); } if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s stopped process %" PRIu64, __FUNCTION__, m_debugged_process_sp->GetID()); // No response required from stop all. return PacketResult::Success; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_memory_read( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } // Parse out the memory address. packet.SetFilePos(strlen("m")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short m packet"); // Read the address. Punting on validation. // FIXME replace with Hex U64 read with no default value that fails on failed // read. const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0); // Validate comma. if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ',')) return SendIllFormedResponse(packet, "Comma sep missing in m packet"); // Get # bytes to read. if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Length missing in m packet"); const uint64_t byte_count = packet.GetHexMaxU64(false, 0); if (byte_count == 0) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s nothing to read: " "zero-length packet", __FUNCTION__); return SendOKResponse(); } // Allocate the response buffer. std::string buf(byte_count, '\0'); if (buf.empty()) return SendErrorResponse(0x78); // Retrieve the process memory. size_t bytes_read = 0; Status error = m_debugged_process_sp->ReadMemoryWithoutTrap( read_addr, &buf[0], byte_count, bytes_read); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " mem 0x%" PRIx64 ": failed to read. Error: %s", __FUNCTION__, m_debugged_process_sp->GetID(), read_addr, error.AsCString()); return SendErrorResponse(0x08); } if (bytes_read == 0) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " mem 0x%" PRIx64 ": read 0 of %" PRIu64 " requested bytes", __FUNCTION__, m_debugged_process_sp->GetID(), read_addr, byte_count); return SendErrorResponse(0x08); } StreamGDBRemote response; packet.SetFilePos(0); char kind = packet.GetChar('?'); if (kind == 'x') response.PutEscapedBytes(buf.data(), byte_count); else { assert(kind == 'm'); for (size_t i = 0; i < bytes_read; ++i) response.PutHex8(buf[i]); } return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_M(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } // Parse out the memory address. packet.SetFilePos(strlen("M")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short M packet"); // Read the address. Punting on validation. // FIXME replace with Hex U64 read with no default value that fails on failed // read. const lldb::addr_t write_addr = packet.GetHexMaxU64(false, 0); // Validate comma. if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ',')) return SendIllFormedResponse(packet, "Comma sep missing in M packet"); // Get # bytes to read. if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Length missing in M packet"); const uint64_t byte_count = packet.GetHexMaxU64(false, 0); if (byte_count == 0) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s nothing to write: " "zero-length packet", __FUNCTION__); return PacketResult::Success; } // Validate colon. if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ':')) return SendIllFormedResponse( packet, "Comma sep missing in M packet after byte length"); // Allocate the conversion buffer. std::vector buf(byte_count, 0); if (buf.empty()) return SendErrorResponse(0x78); // Convert the hex memory write contents to bytes. StreamGDBRemote response; const uint64_t convert_count = packet.GetHexBytes(buf, 0); if (convert_count != byte_count) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " mem 0x%" PRIx64 ": asked to write %" PRIu64 " bytes, but only found %" PRIu64 " to convert.", __FUNCTION__, m_debugged_process_sp->GetID(), write_addr, byte_count, convert_count); return SendIllFormedResponse(packet, "M content byte length specified did " "not match hex-encoded content " "length"); } // Write the process memory. size_t bytes_written = 0; Status error = m_debugged_process_sp->WriteMemory(write_addr, &buf[0], byte_count, bytes_written); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " mem 0x%" PRIx64 ": failed to write. Error: %s", __FUNCTION__, m_debugged_process_sp->GetID(), write_addr, error.AsCString()); return SendErrorResponse(0x09); } if (bytes_written == 0) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " mem 0x%" PRIx64 ": wrote 0 of %" PRIu64 " requested bytes", __FUNCTION__, m_debugged_process_sp->GetID(), write_addr, byte_count); return SendErrorResponse(0x09); } return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfoSupported( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // Currently only the NativeProcessProtocol knows if it can handle a // qMemoryRegionInfoSupported // request, but we're not guaranteed to be attached to a process. For now // we'll assume the // client only asks this when a process is being debugged. // Ensure we have a process running; otherwise, we can't figure this out // since we won't have a NativeProcessProtocol. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } // Test if we can get any region back when asking for the region around NULL. MemoryRegionInfo region_info; const Status error = m_debugged_process_sp->GetMemoryRegionInfo(0, region_info); if (error.Fail()) { // We don't support memory region info collection for this // NativeProcessProtocol. return SendUnimplementedResponse(""); } return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfo( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // Ensure we have a process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } // Parse out the memory address. packet.SetFilePos(strlen("qMemoryRegionInfo:")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short qMemoryRegionInfo: packet"); // Read the address. Punting on validation. const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0); StreamGDBRemote response; // Get the memory region info for the target address. MemoryRegionInfo region_info; const Status error = m_debugged_process_sp->GetMemoryRegionInfo(read_addr, region_info); if (error.Fail()) { // Return the error message. response.PutCString("error:"); response.PutCStringAsRawHex8(error.AsCString()); response.PutChar(';'); } else { // Range start and size. response.Printf("start:%" PRIx64 ";size:%" PRIx64 ";", region_info.GetRange().GetRangeBase(), region_info.GetRange().GetByteSize()); // Permissions. if (region_info.GetReadable() || region_info.GetWritable() || region_info.GetExecutable()) { // Write permissions info. response.PutCString("permissions:"); if (region_info.GetReadable()) response.PutChar('r'); if (region_info.GetWritable()) response.PutChar('w'); if (region_info.GetExecutable()) response.PutChar('x'); response.PutChar(';'); } // Name ConstString name = region_info.GetName(); if (name) { response.PutCString("name:"); response.PutCStringAsRawHex8(name.AsCString()); response.PutChar(';'); } } return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_Z(StringExtractorGDBRemote &packet) { // Ensure we have a process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } // Parse out software or hardware breakpoint or watchpoint requested. packet.SetFilePos(strlen("Z")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse( packet, "Too short Z packet, missing software/hardware specifier"); bool want_breakpoint = true; bool want_hardware = false; uint32_t watch_flags = 0; const GDBStoppointType stoppoint_type = GDBStoppointType(packet.GetS32(eStoppointInvalid)); switch (stoppoint_type) { case eBreakpointSoftware: want_hardware = false; want_breakpoint = true; break; case eBreakpointHardware: want_hardware = true; want_breakpoint = true; break; case eWatchpointWrite: watch_flags = 1; want_hardware = true; want_breakpoint = false; break; case eWatchpointRead: watch_flags = 2; want_hardware = true; want_breakpoint = false; break; case eWatchpointReadWrite: watch_flags = 3; want_hardware = true; want_breakpoint = false; break; case eStoppointInvalid: return SendIllFormedResponse( packet, "Z packet had invalid software/hardware specifier"); } if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',') return SendIllFormedResponse( packet, "Malformed Z packet, expecting comma after stoppoint type"); // Parse out the stoppoint address. if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short Z packet, missing address"); const lldb::addr_t addr = packet.GetHexMaxU64(false, 0); if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',') return SendIllFormedResponse( packet, "Malformed Z packet, expecting comma after address"); // Parse out the stoppoint size (i.e. size hint for opcode size). const uint32_t size = packet.GetHexMaxU32(false, std::numeric_limits::max()); if (size == std::numeric_limits::max()) return SendIllFormedResponse( packet, "Malformed Z packet, failed to parse size argument"); if (want_breakpoint) { // Try to set the breakpoint. const Status error = m_debugged_process_sp->SetBreakpoint(addr, size, want_hardware); if (error.Success()) return SendOKResponse(); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " failed to set breakpoint: %s", __FUNCTION__, m_debugged_process_sp->GetID(), error.AsCString()); return SendErrorResponse(0x09); } else { // Try to set the watchpoint. const Status error = m_debugged_process_sp->SetWatchpoint( addr, size, watch_flags, want_hardware); if (error.Success()) return SendOKResponse(); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " failed to set watchpoint: %s", __FUNCTION__, m_debugged_process_sp->GetID(), error.AsCString()); return SendErrorResponse(0x09); } } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_z(StringExtractorGDBRemote &packet) { // Ensure we have a process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } // Parse out software or hardware breakpoint or watchpoint requested. packet.SetFilePos(strlen("z")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse( packet, "Too short z packet, missing software/hardware specifier"); bool want_breakpoint = true; bool want_hardware = false; const GDBStoppointType stoppoint_type = GDBStoppointType(packet.GetS32(eStoppointInvalid)); switch (stoppoint_type) { case eBreakpointHardware: want_breakpoint = true; want_hardware = true; break; case eBreakpointSoftware: want_breakpoint = true; break; case eWatchpointWrite: want_breakpoint = false; break; case eWatchpointRead: want_breakpoint = false; break; case eWatchpointReadWrite: want_breakpoint = false; break; default: return SendIllFormedResponse( packet, "z packet had invalid software/hardware specifier"); } if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',') return SendIllFormedResponse( packet, "Malformed z packet, expecting comma after stoppoint type"); // Parse out the stoppoint address. if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short z packet, missing address"); const lldb::addr_t addr = packet.GetHexMaxU64(false, 0); if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',') return SendIllFormedResponse( packet, "Malformed z packet, expecting comma after address"); /* // Parse out the stoppoint size (i.e. size hint for opcode size). const uint32_t size = packet.GetHexMaxU32 (false, std::numeric_limits::max ()); if (size == std::numeric_limits::max ()) return SendIllFormedResponse(packet, "Malformed z packet, failed to parse size argument"); */ if (want_breakpoint) { // Try to clear the breakpoint. const Status error = m_debugged_process_sp->RemoveBreakpoint(addr, want_hardware); if (error.Success()) return SendOKResponse(); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " failed to remove breakpoint: %s", __FUNCTION__, m_debugged_process_sp->GetID(), error.AsCString()); return SendErrorResponse(0x09); } else { // Try to clear the watchpoint. const Status error = m_debugged_process_sp->RemoveWatchpoint(addr); if (error.Success()) return SendOKResponse(); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " failed to remove watchpoint: %s", __FUNCTION__, m_debugged_process_sp->GetID(), error.AsCString()); return SendErrorResponse(0x09); } } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_s(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); // Ensure we have a process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x32); } // We first try to use a continue thread id. If any one or any all set, use // the current thread. // Bail out if we don't have a thread id. lldb::tid_t tid = GetContinueThreadID(); if (tid == 0 || tid == LLDB_INVALID_THREAD_ID) tid = GetCurrentThreadID(); if (tid == LLDB_INVALID_THREAD_ID) return SendErrorResponse(0x33); // Double check that we have such a thread. // TODO investigate: on MacOSX we might need to do an UpdateThreads () here. NativeThreadProtocolSP thread_sp = m_debugged_process_sp->GetThreadByID(tid); if (!thread_sp || thread_sp->GetID() != tid) return SendErrorResponse(0x33); // Create the step action for the given thread. ResumeAction action = {tid, eStateStepping, 0}; // Setup the actions list. ResumeActionList actions; actions.Append(action); // All other threads stop while we're single stepping a thread. actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0); Status error = m_debugged_process_sp->Resume(actions); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " Resume() failed with error: %s", __FUNCTION__, m_debugged_process_sp->GetID(), tid, error.AsCString()); return SendErrorResponse(0x49); } // No response here - the stop or exit will come from the resulting action. return PacketResult::Success; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qXfer_auxv_read( StringExtractorGDBRemote &packet) { // *BSD impls should be able to do this too. #if defined(__linux__) || defined(__NetBSD__) Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // Parse out the offset. packet.SetFilePos(strlen("qXfer:auxv:read::")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "qXfer:auxv:read:: packet missing offset"); const uint64_t auxv_offset = packet.GetHexMaxU64(false, std::numeric_limits::max()); if (auxv_offset == std::numeric_limits::max()) return SendIllFormedResponse(packet, "qXfer:auxv:read:: packet missing offset"); // Parse out comma. if (packet.GetBytesLeft() < 1 || packet.GetChar() != ',') return SendIllFormedResponse( packet, "qXfer:auxv:read:: packet missing comma after offset"); // Parse out the length. const uint64_t auxv_length = packet.GetHexMaxU64(false, std::numeric_limits::max()); if (auxv_length == std::numeric_limits::max()) return SendIllFormedResponse(packet, "qXfer:auxv:read:: packet missing length"); // Grab the auxv data if we need it. if (!m_active_auxv_buffer_up) { // Make sure we have a valid process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x10); } // Grab the auxv data. auto buffer_or_error = m_debugged_process_sp->GetAuxvData(); if (!buffer_or_error) { std::error_code ec = buffer_or_error.getError(); LLDB_LOG(log, "no auxv data retrieved: {0}", ec.message()); return SendErrorResponse(ec.value()); } m_active_auxv_buffer_up = std::move(*buffer_or_error); } StreamGDBRemote response; bool done_with_buffer = false; llvm::StringRef buffer = m_active_auxv_buffer_up->getBuffer(); if (auxv_offset >= buffer.size()) { // We have nothing left to send. Mark the buffer as complete. response.PutChar('l'); done_with_buffer = true; } else { // Figure out how many bytes are available starting at the given offset. buffer = buffer.drop_front(auxv_offset); // Mark the response type according to whether we're reading the remainder // of the auxv data. if (auxv_length >= buffer.size()) { // There will be nothing left to read after this response.PutChar('l'); done_with_buffer = true; } else { // There will still be bytes to read after this request. response.PutChar('m'); buffer = buffer.take_front(auxv_length); } // Now write the data in encoded binary form. response.PutEscapedBytes(buffer.data(), buffer.size()); } if (done_with_buffer) m_active_auxv_buffer_up.reset(); return SendPacketNoLock(response.GetString()); #else return SendUnimplementedResponse("not implemented on this platform"); #endif } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_QSaveRegisterState( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); // Move past packet name. packet.SetFilePos(strlen("QSaveRegisterState")); // Get the thread to use. NativeThreadProtocolSP thread_sp = GetThreadFromSuffix(packet); if (!thread_sp) { if (m_thread_suffix_supported) return SendIllFormedResponse( packet, "No thread specified in QSaveRegisterState packet"); else return SendIllFormedResponse(packet, "No thread was is set with the Hg packet"); } // Grab the register context for the thread. NativeRegisterContextSP reg_context_sp(thread_sp->GetRegisterContext()); if (!reg_context_sp) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " failed, no register context available for the thread", __FUNCTION__, m_debugged_process_sp->GetID(), thread_sp->GetID()); return SendErrorResponse(0x15); } // Save registers to a buffer. DataBufferSP register_data_sp; Status error = reg_context_sp->ReadAllRegisterValues(register_data_sp); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " failed to save all register values: %s", __FUNCTION__, m_debugged_process_sp->GetID(), error.AsCString()); return SendErrorResponse(0x75); } // Allocate a new save id. const uint32_t save_id = GetNextSavedRegistersID(); assert((m_saved_registers_map.find(save_id) == m_saved_registers_map.end()) && "GetNextRegisterSaveID() returned an existing register save id"); // Save the register data buffer under the save id. { std::lock_guard guard(m_saved_registers_mutex); m_saved_registers_map[save_id] = register_data_sp; } // Write the response. StreamGDBRemote response; response.Printf("%" PRIu32, save_id); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_QRestoreRegisterState( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); // Parse out save id. packet.SetFilePos(strlen("QRestoreRegisterState:")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse( packet, "QRestoreRegisterState packet missing register save id"); const uint32_t save_id = packet.GetU32(0); if (save_id == 0) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s QRestoreRegisterState " "packet has malformed save id, expecting decimal uint32_t", __FUNCTION__); return SendErrorResponse(0x76); } // Get the thread to use. NativeThreadProtocolSP thread_sp = GetThreadFromSuffix(packet); if (!thread_sp) { if (m_thread_suffix_supported) return SendIllFormedResponse( packet, "No thread specified in QRestoreRegisterState packet"); else return SendIllFormedResponse(packet, "No thread was is set with the Hg packet"); } // Grab the register context for the thread. NativeRegisterContextSP reg_context_sp(thread_sp->GetRegisterContext()); if (!reg_context_sp) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " failed, no register context available for the thread", __FUNCTION__, m_debugged_process_sp->GetID(), thread_sp->GetID()); return SendErrorResponse(0x15); } // Retrieve register state buffer, then remove from the list. DataBufferSP register_data_sp; { std::lock_guard guard(m_saved_registers_mutex); // Find the register set buffer for the given save id. auto it = m_saved_registers_map.find(save_id); if (it == m_saved_registers_map.end()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " does not have a register set save buffer for id %" PRIu32, __FUNCTION__, m_debugged_process_sp->GetID(), save_id); return SendErrorResponse(0x77); } register_data_sp = it->second; // Remove it from the map. m_saved_registers_map.erase(it); } Status error = reg_context_sp->WriteAllRegisterValues(register_data_sp); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " failed to restore all register values: %s", __FUNCTION__, m_debugged_process_sp->GetID(), error.AsCString()); return SendErrorResponse(0x77); } return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_vAttach( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // Consume the ';' after vAttach. packet.SetFilePos(strlen("vAttach")); if (!packet.GetBytesLeft() || packet.GetChar() != ';') return SendIllFormedResponse(packet, "vAttach missing expected ';'"); // Grab the PID to which we will attach (assume hex encoding). lldb::pid_t pid = packet.GetU32(LLDB_INVALID_PROCESS_ID, 16); if (pid == LLDB_INVALID_PROCESS_ID) return SendIllFormedResponse(packet, "vAttach failed to parse the process id"); // Attempt to attach. if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s attempting to attach to " "pid %" PRIu64, __FUNCTION__, pid); Status error = AttachToProcess(pid); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to attach to " "pid %" PRIu64 ": %s\n", __FUNCTION__, pid, error.AsCString()); return SendErrorResponse(0x01); } // Notify we attached by sending a stop packet. return SendStopReasonForState(m_debugged_process_sp->GetState()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_D(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); StopSTDIOForwarding(); // Fail if we don't have a current process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) { if (log) log->Printf( "GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); return SendErrorResponse(0x15); } lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; // Consume the ';' after D. packet.SetFilePos(1); if (packet.GetBytesLeft()) { if (packet.GetChar() != ';') return SendIllFormedResponse(packet, "D missing expected ';'"); // Grab the PID from which we will detach (assume hex encoding). pid = packet.GetU32(LLDB_INVALID_PROCESS_ID, 16); if (pid == LLDB_INVALID_PROCESS_ID) return SendIllFormedResponse(packet, "D failed to parse the process id"); } if (pid != LLDB_INVALID_PROCESS_ID && m_debugged_process_sp->GetID() != pid) { return SendIllFormedResponse(packet, "Invalid pid"); } const Status error = m_debugged_process_sp->Detach(); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to detach from " "pid %" PRIu64 ": %s\n", __FUNCTION__, m_debugged_process_sp->GetID(), error.AsCString()); return SendErrorResponse(0x01); } return SendOKResponse(); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo( StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); packet.SetFilePos(strlen("qThreadStopInfo")); const lldb::tid_t tid = packet.GetHexMaxU32(false, LLDB_INVALID_THREAD_ID); if (tid == LLDB_INVALID_THREAD_ID) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, could not " "parse thread id from request \"%s\"", __FUNCTION__, packet.GetStringRef().c_str()); return SendErrorResponse(0x15); } return SendStopReplyPacketForThread(tid); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo( StringExtractorGDBRemote &) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); // Ensure we have a debugged process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID)) return SendErrorResponse(50); if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s preparing packet for pid " "%" PRIu64, __FUNCTION__, m_debugged_process_sp->GetID()); StreamString response; const bool threads_with_valid_stop_info_only = false; JSONArray::SP threads_array_sp = GetJSONThreadsInfo( *m_debugged_process_sp, threads_with_valid_stop_info_only); if (!threads_array_sp) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to prepare a " "packet for pid %" PRIu64, __FUNCTION__, m_debugged_process_sp->GetID()); return SendErrorResponse(52); } threads_array_sp->Write(response); StreamGDBRemote escaped_response; escaped_response.PutEscapedBytes(response.GetData(), response.GetSize()); return SendPacketNoLock(escaped_response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo( StringExtractorGDBRemote &packet) { // Fail if we don't have a current process. if (!m_debugged_process_sp || m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID) return SendErrorResponse(68); packet.SetFilePos(strlen("qWatchpointSupportInfo")); if (packet.GetBytesLeft() == 0) return SendOKResponse(); if (packet.GetChar() != ':') return SendErrorResponse(67); auto hw_debug_cap = m_debugged_process_sp->GetHardwareDebugSupportInfo(); StreamGDBRemote response; if (hw_debug_cap == llvm::None) response.Printf("num:0;"); else response.Printf("num:%d;", hw_debug_cap->second); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress( StringExtractorGDBRemote &packet) { // Fail if we don't have a current process. if (!m_debugged_process_sp || m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID) return SendErrorResponse(67); packet.SetFilePos(strlen("qFileLoadAddress:")); if (packet.GetBytesLeft() == 0) return SendErrorResponse(68); std::string file_name; packet.GetHexByteString(file_name); lldb::addr_t file_load_address = LLDB_INVALID_ADDRESS; Status error = m_debugged_process_sp->GetFileLoadAddress(file_name, file_load_address); if (error.Fail()) return SendErrorResponse(69); if (file_load_address == LLDB_INVALID_ADDRESS) return SendErrorResponse(1); // File not loaded StreamGDBRemote response; response.PutHex64(file_load_address); return SendPacketNoLock(response.GetString()); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_QPassSignals( StringExtractorGDBRemote &packet) { std::vector signals; packet.SetFilePos(strlen("QPassSignals:")); // Read sequence of hex signal numbers divided by a semicolon and // optionally spaces. while (packet.GetBytesLeft() > 0) { int signal = packet.GetS32(-1, 16); if (signal < 0) return SendIllFormedResponse(packet, "Failed to parse signal number."); signals.push_back(signal); packet.SkipSpaces(); char separator = packet.GetChar(); if (separator == '\0') break; // End of string if (separator != ';') return SendIllFormedResponse(packet, "Invalid separator," " expected semicolon."); } // Fail if we don't have a current process. if (!m_debugged_process_sp) return SendErrorResponse(68); Status error = m_debugged_process_sp->IgnoreSignals(signals); if (error.Fail()) return SendErrorResponse(69); return SendOKResponse(); } void GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection() { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // Tell the stdio connection to shut down. if (m_stdio_communication.IsConnected()) { auto connection = m_stdio_communication.GetConnection(); if (connection) { Status error; connection->Disconnect(&error); if (error.Success()) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s disconnect process " "terminal stdio - SUCCESS", __FUNCTION__); } else { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s disconnect process " "terminal stdio - FAIL: %s", __FUNCTION__, error.AsCString()); } } } } NativeThreadProtocolSP GDBRemoteCommunicationServerLLGS::GetThreadFromSuffix( StringExtractorGDBRemote &packet) { NativeThreadProtocolSP thread_sp; // We have no thread if we don't have a process. if (!m_debugged_process_sp || m_debugged_process_sp->GetID() == LLDB_INVALID_PROCESS_ID) return thread_sp; // If the client hasn't asked for thread suffix support, there will not be a // thread suffix. // Use the current thread in that case. if (!m_thread_suffix_supported) { const lldb::tid_t current_tid = GetCurrentThreadID(); if (current_tid == LLDB_INVALID_THREAD_ID) return thread_sp; else if (current_tid == 0) { // Pick a thread. return m_debugged_process_sp->GetThreadAtIndex(0); } else return m_debugged_process_sp->GetThreadByID(current_tid); } Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); // Parse out the ';'. if (packet.GetBytesLeft() < 1 || packet.GetChar() != ';') { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s gdb-remote parse " "error: expected ';' prior to start of thread suffix: packet " "contents = '%s'", __FUNCTION__, packet.GetStringRef().c_str()); return thread_sp; } if (!packet.GetBytesLeft()) return thread_sp; // Parse out thread: portion. if (strncmp(packet.Peek(), "thread:", strlen("thread:")) != 0) { if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s gdb-remote parse " "error: expected 'thread:' but not found, packet contents = " "'%s'", __FUNCTION__, packet.GetStringRef().c_str()); return thread_sp; } packet.SetFilePos(packet.GetFilePos() + strlen("thread:")); const lldb::tid_t tid = packet.GetHexMaxU64(false, 0); if (tid != 0) return m_debugged_process_sp->GetThreadByID(tid); return thread_sp; } lldb::tid_t GDBRemoteCommunicationServerLLGS::GetCurrentThreadID() const { if (m_current_tid == 0 || m_current_tid == LLDB_INVALID_THREAD_ID) { // Use whatever the debug process says is the current thread id // since the protocol either didn't specify or specified we want // any/all threads marked as the current thread. if (!m_debugged_process_sp) return LLDB_INVALID_THREAD_ID; return m_debugged_process_sp->GetCurrentThreadID(); } // Use the specific current thread id set by the gdb remote protocol. return m_current_tid; } uint32_t GDBRemoteCommunicationServerLLGS::GetNextSavedRegistersID() { std::lock_guard guard(m_saved_registers_mutex); return m_next_saved_registers_id++; } void GDBRemoteCommunicationServerLLGS::ClearProcessSpecificData() { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); LLDB_LOG(log, "clearing auxv buffer: {0}", m_active_auxv_buffer_up.get()); m_active_auxv_buffer_up.reset(); } FileSpec GDBRemoteCommunicationServerLLGS::FindModuleFile(const std::string &module_path, const ArchSpec &arch) { if (m_debugged_process_sp) { FileSpec file_spec; if (m_debugged_process_sp ->GetLoadedModuleFileSpec(module_path.c_str(), file_spec) .Success()) { if (file_spec.Exists()) return file_spec; } } return GDBRemoteCommunicationServerCommon::FindModuleFile(module_path, arch); } Index: vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h (revision 319149) +++ vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h (revision 319150) @@ -1,253 +1,261 @@ //===-- GDBRemoteCommunicationServerLLGS.h ----------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_GDBRemoteCommunicationServerLLGS_h_ #define liblldb_GDBRemoteCommunicationServerLLGS_h_ // C Includes // C++ Includes #include #include // Other libraries and framework includes #include "lldb/Core/Communication.h" #include "lldb/Host/MainLoop.h" #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/lldb-private-forward.h" // Project includes #include "GDBRemoteCommunicationServerCommon.h" class StringExtractorGDBRemote; namespace lldb_private { namespace process_gdb_remote { class ProcessGDBRemote; class GDBRemoteCommunicationServerLLGS : public GDBRemoteCommunicationServerCommon, public NativeProcessProtocol::NativeDelegate { public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ GDBRemoteCommunicationServerLLGS(MainLoop &mainloop); //------------------------------------------------------------------ /// Specify the program to launch and its arguments. /// /// @param[in] args /// The command line to launch. /// /// @param[in] argc /// The number of elements in the args array of cstring pointers. /// /// @return /// An Status object indicating the success or failure of making /// the setting. //------------------------------------------------------------------ Status SetLaunchArguments(const char *const args[], int argc); //------------------------------------------------------------------ /// Specify the launch flags for the process. /// /// @param[in] launch_flags /// The launch flags to use when launching this process. /// /// @return /// An Status object indicating the success or failure of making /// the setting. //------------------------------------------------------------------ Status SetLaunchFlags(unsigned int launch_flags); //------------------------------------------------------------------ /// Launch a process with the current launch settings. /// /// This method supports running an lldb-gdbserver or similar /// server in a situation where the startup code has been provided /// with all the information for a child process to be launched. /// /// @return /// An Status object indicating the success or failure of the /// launch. //------------------------------------------------------------------ Status LaunchProcess() override; //------------------------------------------------------------------ /// Attach to a process. /// /// This method supports attaching llgs to a process accessible via the /// configured Platform. /// /// @return /// An Status object indicating the success or failure of the /// attach operation. //------------------------------------------------------------------ Status AttachToProcess(lldb::pid_t pid); //------------------------------------------------------------------ // NativeProcessProtocol::NativeDelegate overrides //------------------------------------------------------------------ void InitializeDelegate(NativeProcessProtocol *process) override; void ProcessStateChanged(NativeProcessProtocol *process, lldb::StateType state) override; void DidExec(NativeProcessProtocol *process) override; Status InitializeConnection(std::unique_ptr &&connection); protected: MainLoop &m_mainloop; MainLoop::ReadHandleUP m_network_handle_up; lldb::tid_t m_current_tid; lldb::tid_t m_continue_tid; std::recursive_mutex m_debugged_process_mutex; NativeProcessProtocolSP m_debugged_process_sp; Communication m_stdio_communication; MainLoop::ReadHandleUP m_stdio_handle_up; lldb::StateType m_inferior_prev_state; std::unique_ptr m_active_auxv_buffer_up; std::mutex m_saved_registers_mutex; std::unordered_map m_saved_registers_map; uint32_t m_next_saved_registers_id; bool m_handshake_completed : 1; PacketResult SendONotification(const char *buffer, uint32_t len); PacketResult SendWResponse(NativeProcessProtocol *process); PacketResult SendStopReplyPacketForThread(lldb::tid_t tid); PacketResult SendStopReasonForState(lldb::StateType process_state); PacketResult Handle_k(StringExtractorGDBRemote &packet); PacketResult Handle_qProcessInfo(StringExtractorGDBRemote &packet); PacketResult Handle_qC(StringExtractorGDBRemote &packet); PacketResult Handle_QSetDisableASLR(StringExtractorGDBRemote &packet); PacketResult Handle_QSetWorkingDir(StringExtractorGDBRemote &packet); PacketResult Handle_qGetWorkingDir(StringExtractorGDBRemote &packet); PacketResult Handle_C(StringExtractorGDBRemote &packet); PacketResult Handle_c(StringExtractorGDBRemote &packet); PacketResult Handle_vCont(StringExtractorGDBRemote &packet); PacketResult Handle_vCont_actions(StringExtractorGDBRemote &packet); PacketResult Handle_stop_reason(StringExtractorGDBRemote &packet); PacketResult Handle_qRegisterInfo(StringExtractorGDBRemote &packet); PacketResult Handle_qfThreadInfo(StringExtractorGDBRemote &packet); PacketResult Handle_qsThreadInfo(StringExtractorGDBRemote &packet); PacketResult Handle_p(StringExtractorGDBRemote &packet); PacketResult Handle_P(StringExtractorGDBRemote &packet); PacketResult Handle_H(StringExtractorGDBRemote &packet); PacketResult Handle_I(StringExtractorGDBRemote &packet); PacketResult Handle_interrupt(StringExtractorGDBRemote &packet); // Handles $m and $x packets. PacketResult Handle_memory_read(StringExtractorGDBRemote &packet); PacketResult Handle_M(StringExtractorGDBRemote &packet); PacketResult Handle_qMemoryRegionInfoSupported(StringExtractorGDBRemote &packet); PacketResult Handle_qMemoryRegionInfo(StringExtractorGDBRemote &packet); PacketResult Handle_Z(StringExtractorGDBRemote &packet); PacketResult Handle_z(StringExtractorGDBRemote &packet); PacketResult Handle_s(StringExtractorGDBRemote &packet); PacketResult Handle_qXfer_auxv_read(StringExtractorGDBRemote &packet); PacketResult Handle_QSaveRegisterState(StringExtractorGDBRemote &packet); + PacketResult Handle_jTraceStart(StringExtractorGDBRemote &packet); + + PacketResult Handle_jTraceRead(StringExtractorGDBRemote &packet); + + PacketResult Handle_jTraceStop(StringExtractorGDBRemote &packet); + + PacketResult Handle_jTraceConfigRead(StringExtractorGDBRemote &packet); + PacketResult Handle_QRestoreRegisterState(StringExtractorGDBRemote &packet); PacketResult Handle_vAttach(StringExtractorGDBRemote &packet); PacketResult Handle_D(StringExtractorGDBRemote &packet); PacketResult Handle_qThreadStopInfo(StringExtractorGDBRemote &packet); PacketResult Handle_jThreadsInfo(StringExtractorGDBRemote &packet); PacketResult Handle_qWatchpointSupportInfo(StringExtractorGDBRemote &packet); PacketResult Handle_qFileLoadAddress(StringExtractorGDBRemote &packet); PacketResult Handle_QPassSignals(StringExtractorGDBRemote &packet); void SetCurrentThreadID(lldb::tid_t tid); lldb::tid_t GetCurrentThreadID() const; void SetContinueThreadID(lldb::tid_t tid); lldb::tid_t GetContinueThreadID() const { return m_continue_tid; } Status SetSTDIOFileDescriptor(int fd); FileSpec FindModuleFile(const std::string &module_path, const ArchSpec &arch) override; private: void HandleInferiorState_Exited(NativeProcessProtocol *process); void HandleInferiorState_Stopped(NativeProcessProtocol *process); NativeThreadProtocolSP GetThreadFromSuffix(StringExtractorGDBRemote &packet); uint32_t GetNextSavedRegistersID(); void MaybeCloseInferiorTerminalConnection(); void ClearProcessSpecificData(); void RegisterPacketHandlers(); void DataAvailableCallback(); void SendProcessOutput(); void StartSTDIOForwarding(); void StopSTDIOForwarding(); //------------------------------------------------------------------ // For GDBRemoteCommunicationServerLLGS only //------------------------------------------------------------------ DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationServerLLGS); }; } // namespace process_gdb_remote } // namespace lldb_private #endif // liblldb_GDBRemoteCommunicationServerLLGS_h_ Index: vendor/lldb/dist/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp (revision 319149) +++ vendor/lldb/dist/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp (revision 319150) @@ -1,5198 +1,5224 @@ //===-- ProcessGDBRemote.cpp ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Host/Config.h" // C Includes #include #include #ifndef LLDB_DISABLE_POSIX #include #include // for mmap #include #endif #include #include #include // C++ Includes #include #include #include #include #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/Timer.h" #include "lldb/Core/Value.h" #include "lldb/DataFormatters/FormatManager.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/HostThread.h" #include "lldb/Host/PseudoTerminal.h" #include "lldb/Host/StringConvert.h" #include "lldb/Host/Symbols.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/XML.h" #include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionGroupBoolean.h" #include "lldb/Interpreter/OptionGroupUInt64.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/Options.h" #include "lldb/Interpreter/Property.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/ABI.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/TargetList.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Utility/CleanUp.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/StreamString.h" // Project includes #include "GDBRemoteRegisterContext.h" #include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h" #include "Plugins/Process/Utility/GDBRemoteSignals.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" #include "Plugins/Process/Utility/StopInfoMachException.h" #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "ThreadGDBRemote.h" #include "Utility/StringExtractorGDBRemote.h" #include "lldb/Host/Host.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Threading.h" #include "llvm/Support/raw_ostream.h" #define DEBUGSERVER_BASENAME "debugserver" using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_gdb_remote; namespace lldb { // Provide a function that can easily dump the packet history if we know a // ProcessGDBRemote * value (which we can get from logs or from debugging). // We need the function in the lldb namespace so it makes it into the final // executable since the LLDB shared library only exports stuff in the lldb // namespace. This allows you to attach with a debugger and call this // function and get the packet history dumped to a file. void DumpProcessGDBRemotePacketHistory(void *p, const char *path) { StreamFile strm; Status error(strm.GetFile().Open(path, File::eOpenOptionWrite | File::eOpenOptionCanCreate)); if (error.Success()) ((ProcessGDBRemote *)p)->GetGDBRemote().DumpHistory(strm); } } namespace { static PropertyDefinition g_properties[] = { {"packet-timeout", OptionValue::eTypeUInt64, true, 1, NULL, NULL, "Specify the default packet timeout in seconds."}, {"target-definition-file", OptionValue::eTypeFileSpec, true, 0, NULL, NULL, "The file that provides the description for remote target registers."}, {NULL, OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL}}; enum { ePropertyPacketTimeout, ePropertyTargetDefinitionFile }; class PluginProperties : public Properties { public: static ConstString GetSettingName() { return ProcessGDBRemote::GetPluginNameStatic(); } PluginProperties() : Properties() { m_collection_sp.reset(new OptionValueProperties(GetSettingName())); m_collection_sp->Initialize(g_properties); } virtual ~PluginProperties() {} uint64_t GetPacketTimeout() { const uint32_t idx = ePropertyPacketTimeout; return m_collection_sp->GetPropertyAtIndexAsUInt64( NULL, idx, g_properties[idx].default_uint_value); } bool SetPacketTimeout(uint64_t timeout) { const uint32_t idx = ePropertyPacketTimeout; return m_collection_sp->SetPropertyAtIndexAsUInt64(NULL, idx, timeout); } FileSpec GetTargetDefinitionFile() const { const uint32_t idx = ePropertyTargetDefinitionFile; return m_collection_sp->GetPropertyAtIndexAsFileSpec(NULL, idx); } }; typedef std::shared_ptr ProcessKDPPropertiesSP; static const ProcessKDPPropertiesSP &GetGlobalPluginProperties() { static ProcessKDPPropertiesSP g_settings_sp; if (!g_settings_sp) g_settings_sp.reset(new PluginProperties()); return g_settings_sp; } } // anonymous namespace end // TODO Randomly assigning a port is unsafe. We should get an unused // ephemeral port from the kernel and make sure we reserve it before passing // it to debugserver. #if defined(__APPLE__) #define LOW_PORT (IPPORT_RESERVED) #define HIGH_PORT (IPPORT_HIFIRSTAUTO) #else #define LOW_PORT (1024u) #define HIGH_PORT (49151u) #endif #if defined(__APPLE__) && \ (defined(__arm__) || defined(__arm64__) || defined(__aarch64__)) static bool rand_initialized = false; static inline uint16_t get_random_port() { if (!rand_initialized) { time_t seed = time(NULL); rand_initialized = true; srand(seed); } return (rand() % (HIGH_PORT - LOW_PORT)) + LOW_PORT; } #endif ConstString ProcessGDBRemote::GetPluginNameStatic() { static ConstString g_name("gdb-remote"); return g_name; } const char *ProcessGDBRemote::GetPluginDescriptionStatic() { return "GDB Remote protocol based debugging plug-in."; } void ProcessGDBRemote::Terminate() { PluginManager::UnregisterPlugin(ProcessGDBRemote::CreateInstance); } lldb::ProcessSP ProcessGDBRemote::CreateInstance(lldb::TargetSP target_sp, ListenerSP listener_sp, const FileSpec *crash_file_path) { lldb::ProcessSP process_sp; if (crash_file_path == NULL) process_sp.reset(new ProcessGDBRemote(target_sp, listener_sp)); return process_sp; } bool ProcessGDBRemote::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) { if (plugin_specified_by_name) return true; // For now we are just making sure the file exists for a given module Module *exe_module = target_sp->GetExecutableModulePointer(); if (exe_module) { ObjectFile *exe_objfile = exe_module->GetObjectFile(); // We can't debug core files... switch (exe_objfile->GetType()) { case ObjectFile::eTypeInvalid: case ObjectFile::eTypeCoreFile: case ObjectFile::eTypeDebugInfo: case ObjectFile::eTypeObjectFile: case ObjectFile::eTypeSharedLibrary: case ObjectFile::eTypeStubLibrary: case ObjectFile::eTypeJIT: return false; case ObjectFile::eTypeExecutable: case ObjectFile::eTypeDynamicLinker: case ObjectFile::eTypeUnknown: break; } return exe_module->GetFileSpec().Exists(); } // However, if there is no executable module, we return true since we might be // preparing to attach. return true; } //---------------------------------------------------------------------- // ProcessGDBRemote constructor //---------------------------------------------------------------------- ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp, ListenerSP listener_sp) : Process(target_sp, listener_sp), m_flags(0), m_gdb_comm(), m_debugserver_pid(LLDB_INVALID_PROCESS_ID), m_last_stop_packet_mutex(), m_register_info(), m_async_broadcaster(NULL, "lldb.process.gdb-remote.async-broadcaster"), m_async_listener_sp( Listener::MakeListener("lldb.process.gdb-remote.async-listener")), m_async_thread_state_mutex(), m_thread_ids(), m_thread_pcs(), m_jstopinfo_sp(), m_jthreadsinfo_sp(), m_continue_c_tids(), m_continue_C_tids(), m_continue_s_tids(), m_continue_S_tids(), m_max_memory_size(0), m_remote_stub_max_memory_size(0), m_addr_to_mmap_size(), m_thread_create_bp_sp(), m_waiting_for_attach(false), m_destroy_tried_resuming(false), m_command_sp(), m_breakpoint_pc_offset(0), m_initial_tid(LLDB_INVALID_THREAD_ID) { m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue, "async thread continue"); m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadDidExit, "async thread did exit"); Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_ASYNC)); const uint32_t async_event_mask = eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit; if (m_async_listener_sp->StartListeningForEvents( &m_async_broadcaster, async_event_mask) != async_event_mask) { if (log) log->Printf("ProcessGDBRemote::%s failed to listen for " "m_async_broadcaster events", __FUNCTION__); } const uint32_t gdb_event_mask = Communication::eBroadcastBitReadThreadDidExit | GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify; if (m_async_listener_sp->StartListeningForEvents( &m_gdb_comm, gdb_event_mask) != gdb_event_mask) { if (log) log->Printf("ProcessGDBRemote::%s failed to listen for m_gdb_comm events", __FUNCTION__); } const uint64_t timeout_seconds = GetGlobalPluginProperties()->GetPacketTimeout(); if (timeout_seconds > 0) m_gdb_comm.SetPacketTimeout(std::chrono::seconds(timeout_seconds)); } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- ProcessGDBRemote::~ProcessGDBRemote() { // m_mach_process.UnregisterNotificationCallbacks (this); Clear(); // We need to call finalize on the process before destroying ourselves // to make sure all of the broadcaster cleanup goes as planned. If we // destruct this class, then Process::~Process() might have problems // trying to fully destroy the broadcaster. Finalize(); // The general Finalize is going to try to destroy the process and that SHOULD // shut down the async thread. However, if we don't kill it it will get // stranded and // its connection will go away so when it wakes up it will crash. So kill it // for sure here. StopAsyncThread(); KillDebugserverProcess(); } //---------------------------------------------------------------------- // PluginInterface //---------------------------------------------------------------------- ConstString ProcessGDBRemote::GetPluginName() { return GetPluginNameStatic(); } uint32_t ProcessGDBRemote::GetPluginVersion() { return 1; } bool ProcessGDBRemote::ParsePythonTargetDefinition( const FileSpec &target_definition_fspec) { ScriptInterpreter *interpreter = GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); Status error; StructuredData::ObjectSP module_object_sp( interpreter->LoadPluginModule(target_definition_fspec, error)); if (module_object_sp) { StructuredData::DictionarySP target_definition_sp( interpreter->GetDynamicSettings(module_object_sp, &GetTarget(), "gdb-server-target-definition", error)); if (target_definition_sp) { StructuredData::ObjectSP target_object( target_definition_sp->GetValueForKey("host-info")); if (target_object) { if (auto host_info_dict = target_object->GetAsDictionary()) { StructuredData::ObjectSP triple_value = host_info_dict->GetValueForKey("triple"); if (auto triple_string_value = triple_value->GetAsString()) { std::string triple_string = triple_string_value->GetValue(); ArchSpec host_arch(triple_string.c_str()); if (!host_arch.IsCompatibleMatch(GetTarget().GetArchitecture())) { GetTarget().SetArchitecture(host_arch); } } } } m_breakpoint_pc_offset = 0; StructuredData::ObjectSP breakpoint_pc_offset_value = target_definition_sp->GetValueForKey("breakpoint-pc-offset"); if (breakpoint_pc_offset_value) { if (auto breakpoint_pc_int_value = breakpoint_pc_offset_value->GetAsInteger()) m_breakpoint_pc_offset = breakpoint_pc_int_value->GetValue(); } if (m_register_info.SetRegisterInfo(*target_definition_sp, GetTarget().GetArchitecture()) > 0) { return true; } } } return false; } // If the remote stub didn't give us eh_frame or DWARF register numbers for a // register, // see if the ABI can provide them. // DWARF and eh_frame register numbers are defined as a part of the ABI. static void AugmentRegisterInfoViaABI(RegisterInfo ®_info, ConstString reg_name, ABISP abi_sp) { if (reg_info.kinds[eRegisterKindEHFrame] == LLDB_INVALID_REGNUM || reg_info.kinds[eRegisterKindDWARF] == LLDB_INVALID_REGNUM) { if (abi_sp) { RegisterInfo abi_reg_info; if (abi_sp->GetRegisterInfoByName(reg_name, abi_reg_info)) { if (reg_info.kinds[eRegisterKindEHFrame] == LLDB_INVALID_REGNUM && abi_reg_info.kinds[eRegisterKindEHFrame] != LLDB_INVALID_REGNUM) { reg_info.kinds[eRegisterKindEHFrame] = abi_reg_info.kinds[eRegisterKindEHFrame]; } if (reg_info.kinds[eRegisterKindDWARF] == LLDB_INVALID_REGNUM && abi_reg_info.kinds[eRegisterKindDWARF] != LLDB_INVALID_REGNUM) { reg_info.kinds[eRegisterKindDWARF] = abi_reg_info.kinds[eRegisterKindDWARF]; } if (reg_info.kinds[eRegisterKindGeneric] == LLDB_INVALID_REGNUM && abi_reg_info.kinds[eRegisterKindGeneric] != LLDB_INVALID_REGNUM) { reg_info.kinds[eRegisterKindGeneric] = abi_reg_info.kinds[eRegisterKindGeneric]; } } } } } static size_t SplitCommaSeparatedRegisterNumberString( const llvm::StringRef &comma_separated_regiter_numbers, std::vector ®nums, int base) { regnums.clear(); std::pair value_pair; value_pair.second = comma_separated_regiter_numbers; do { value_pair = value_pair.second.split(','); if (!value_pair.first.empty()) { uint32_t reg = StringConvert::ToUInt32(value_pair.first.str().c_str(), LLDB_INVALID_REGNUM, base); if (reg != LLDB_INVALID_REGNUM) regnums.push_back(reg); } } while (!value_pair.second.empty()); return regnums.size(); } void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { if (!force && m_register_info.GetNumRegisters() > 0) return; m_register_info.Clear(); // Check if qHostInfo specified a specific packet timeout for this connection. // If so then lets update our setting so the user knows what the timeout is // and can see it. const auto host_packet_timeout = m_gdb_comm.GetHostDefaultPacketTimeout(); if (host_packet_timeout > std::chrono::seconds(0)) { GetGlobalPluginProperties()->SetPacketTimeout(host_packet_timeout.count()); } // Register info search order: // 1 - Use the target definition python file if one is specified. // 2 - If the target definition doesn't have any of the info from the // target.xml (registers) then proceed to read the target.xml. // 3 - Fall back on the qRegisterInfo packets. FileSpec target_definition_fspec = GetGlobalPluginProperties()->GetTargetDefinitionFile(); if (!target_definition_fspec.Exists()) { // If the filename doesn't exist, it may be a ~ not having been expanded - // try to resolve it. target_definition_fspec.ResolvePath(); } if (target_definition_fspec) { // See if we can get register definitions from a python file if (ParsePythonTargetDefinition(target_definition_fspec)) { return; } else { StreamSP stream_sp = GetTarget().GetDebugger().GetAsyncOutputStream(); stream_sp->Printf("ERROR: target description file %s failed to parse.\n", target_definition_fspec.GetPath().c_str()); } } const ArchSpec &target_arch = GetTarget().GetArchitecture(); const ArchSpec &remote_host_arch = m_gdb_comm.GetHostArchitecture(); const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); // Use the process' architecture instead of the host arch, if available ArchSpec arch_to_use; if (remote_process_arch.IsValid()) arch_to_use = remote_process_arch; else arch_to_use = remote_host_arch; if (!arch_to_use.IsValid()) arch_to_use = target_arch; if (GetGDBServerRegisterInfo(arch_to_use)) return; char packet[128]; uint32_t reg_offset = 0; uint32_t reg_num = 0; for (StringExtractorGDBRemote::ResponseType response_type = StringExtractorGDBRemote::eResponse; response_type == StringExtractorGDBRemote::eResponse; ++reg_num) { const int packet_len = ::snprintf(packet, sizeof(packet), "qRegisterInfo%x", reg_num); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, false) == GDBRemoteCommunication::PacketResult::Success) { response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { llvm::StringRef name; llvm::StringRef value; ConstString reg_name; ConstString alt_name; ConstString set_name; std::vector value_regs; std::vector invalidate_regs; std::vector dwarf_opcode_bytes; RegisterInfo reg_info = { NULL, // Name NULL, // Alt name 0, // byte size reg_offset, // offset eEncodingUint, // encoding eFormatHex, // format { LLDB_INVALID_REGNUM, // eh_frame reg num LLDB_INVALID_REGNUM, // DWARF reg num LLDB_INVALID_REGNUM, // generic reg num reg_num, // process plugin reg num reg_num // native register number }, NULL, NULL, NULL, // Dwarf expression opcode bytes pointer 0 // Dwarf expression opcode bytes length }; while (response.GetNameColonValue(name, value)) { if (name.equals("name")) { reg_name.SetString(value); } else if (name.equals("alt-name")) { alt_name.SetString(value); } else if (name.equals("bitsize")) { value.getAsInteger(0, reg_info.byte_size); reg_info.byte_size /= CHAR_BIT; } else if (name.equals("offset")) { if (value.getAsInteger(0, reg_offset)) reg_offset = UINT32_MAX; } else if (name.equals("encoding")) { const Encoding encoding = Args::StringToEncoding(value); if (encoding != eEncodingInvalid) reg_info.encoding = encoding; } else if (name.equals("format")) { Format format = eFormatInvalid; if (Args::StringToFormat(value.str().c_str(), format, NULL) .Success()) reg_info.format = format; else { reg_info.format = llvm::StringSwitch(value) .Case("binary", eFormatBinary) .Case("decimal", eFormatDecimal) .Case("hex", eFormatHex) .Case("float", eFormatFloat) .Case("vector-sint8", eFormatVectorOfSInt8) .Case("vector-uint8", eFormatVectorOfUInt8) .Case("vector-sint16", eFormatVectorOfSInt16) .Case("vector-uint16", eFormatVectorOfUInt16) .Case("vector-sint32", eFormatVectorOfSInt32) .Case("vector-uint32", eFormatVectorOfUInt32) .Case("vector-float32", eFormatVectorOfFloat32) .Case("vector-uint64", eFormatVectorOfUInt64) .Case("vector-uint128", eFormatVectorOfUInt128) .Default(eFormatInvalid); } } else if (name.equals("set")) { set_name.SetString(value); } else if (name.equals("gcc") || name.equals("ehframe")) { if (value.getAsInteger(0, reg_info.kinds[eRegisterKindEHFrame])) reg_info.kinds[eRegisterKindEHFrame] = LLDB_INVALID_REGNUM; } else if (name.equals("dwarf")) { if (value.getAsInteger(0, reg_info.kinds[eRegisterKindDWARF])) reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM; } else if (name.equals("generic")) { reg_info.kinds[eRegisterKindGeneric] = Args::StringToGenericRegister(value); } else if (name.equals("container-regs")) { SplitCommaSeparatedRegisterNumberString(value, value_regs, 16); } else if (name.equals("invalidate-regs")) { SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 16); } else if (name.equals("dynamic_size_dwarf_expr_bytes")) { size_t dwarf_opcode_len = value.size() / 2; assert(dwarf_opcode_len > 0); dwarf_opcode_bytes.resize(dwarf_opcode_len); reg_info.dynamic_size_dwarf_len = dwarf_opcode_len; StringExtractor opcode_extractor(value); uint32_t ret_val = opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes); assert(dwarf_opcode_len == ret_val); UNUSED_IF_ASSERT_DISABLED(ret_val); reg_info.dynamic_size_dwarf_expr_bytes = dwarf_opcode_bytes.data(); } } reg_info.byte_offset = reg_offset; assert(reg_info.byte_size != 0); reg_offset += reg_info.byte_size; if (!value_regs.empty()) { value_regs.push_back(LLDB_INVALID_REGNUM); reg_info.value_regs = value_regs.data(); } if (!invalidate_regs.empty()) { invalidate_regs.push_back(LLDB_INVALID_REGNUM); reg_info.invalidate_regs = invalidate_regs.data(); } // We have to make a temporary ABI here, and not use the GetABI because // this code // gets called in DidAttach, when the target architecture (and // consequently the ABI we'll get from // the process) may be wrong. ABISP abi_to_use = ABI::FindPlugin(arch_to_use); AugmentRegisterInfoViaABI(reg_info, reg_name, abi_to_use); m_register_info.AddRegister(reg_info, reg_name, alt_name, set_name); } else { break; // ensure exit before reg_num is incremented } } else { break; } } if (m_register_info.GetNumRegisters() > 0) { m_register_info.Finalize(GetTarget().GetArchitecture()); return; } // We didn't get anything if the accumulated reg_num is zero. See if we are // debugging ARM and fill with a hard coded register set until we can get an // updated debugserver down on the devices. // On the other hand, if the accumulated reg_num is positive, see if we can // add composite registers to the existing primordial ones. bool from_scratch = (m_register_info.GetNumRegisters() == 0); if (!target_arch.IsValid()) { if (arch_to_use.IsValid() && (arch_to_use.GetMachine() == llvm::Triple::arm || arch_to_use.GetMachine() == llvm::Triple::thumb) && arch_to_use.GetTriple().getVendor() == llvm::Triple::Apple) m_register_info.HardcodeARMRegisters(from_scratch); } else if (target_arch.GetMachine() == llvm::Triple::arm || target_arch.GetMachine() == llvm::Triple::thumb) { m_register_info.HardcodeARMRegisters(from_scratch); } // At this point, we can finalize our register info. m_register_info.Finalize(GetTarget().GetArchitecture()); } Status ProcessGDBRemote::WillLaunch(Module *module) { return WillLaunchOrAttach(); } Status ProcessGDBRemote::WillAttachToProcessWithID(lldb::pid_t pid) { return WillLaunchOrAttach(); } Status ProcessGDBRemote::WillAttachToProcessWithName(const char *process_name, bool wait_for_launch) { return WillLaunchOrAttach(); } Status ProcessGDBRemote::DoConnectRemote(Stream *strm, llvm::StringRef remote_url) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); Status error(WillLaunchOrAttach()); if (error.Fail()) return error; error = ConnectToDebugserver(remote_url); if (error.Fail()) return error; StartAsyncThread(); lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); if (pid == LLDB_INVALID_PROCESS_ID) { // We don't have a valid process ID, so note that we are connected // and could now request to launch or attach, or get remote process // listings... SetPrivateState(eStateConnected); } else { // We have a valid process SetID(pid); GetThreadList(); StringExtractorGDBRemote response; if (m_gdb_comm.GetStopReply(response)) { SetLastStopPacket(response); // '?' Packets must be handled differently in non-stop mode if (GetTarget().GetNonStopModeEnabled()) HandleStopReplySequence(); Target &target = GetTarget(); if (!target.GetArchitecture().IsValid()) { if (m_gdb_comm.GetProcessArchitecture().IsValid()) { target.SetArchitecture(m_gdb_comm.GetProcessArchitecture()); } else { target.SetArchitecture(m_gdb_comm.GetHostArchitecture()); } } const StateType state = SetThreadStopInfo(response); if (state != eStateInvalid) { SetPrivateState(state); } else error.SetErrorStringWithFormat( "Process %" PRIu64 " was reported after connecting to " "'%s', but state was not stopped: %s", pid, remote_url.str().c_str(), StateAsCString(state)); } else error.SetErrorStringWithFormat("Process %" PRIu64 " was reported after connecting to '%s', " "but no stop reply packet was received", pid, remote_url.str().c_str()); } if (log) log->Printf("ProcessGDBRemote::%s pid %" PRIu64 ": normalizing target architecture initial triple: %s " "(GetTarget().GetArchitecture().IsValid() %s, " "m_gdb_comm.GetHostArchitecture().IsValid(): %s)", __FUNCTION__, GetID(), GetTarget().GetArchitecture().GetTriple().getTriple().c_str(), GetTarget().GetArchitecture().IsValid() ? "true" : "false", m_gdb_comm.GetHostArchitecture().IsValid() ? "true" : "false"); if (error.Success() && !GetTarget().GetArchitecture().IsValid() && m_gdb_comm.GetHostArchitecture().IsValid()) { // Prefer the *process'* architecture over that of the *host*, if available. if (m_gdb_comm.GetProcessArchitecture().IsValid()) GetTarget().SetArchitecture(m_gdb_comm.GetProcessArchitecture()); else GetTarget().SetArchitecture(m_gdb_comm.GetHostArchitecture()); } if (log) log->Printf("ProcessGDBRemote::%s pid %" PRIu64 ": normalized target architecture triple: %s", __FUNCTION__, GetID(), GetTarget().GetArchitecture().GetTriple().getTriple().c_str()); if (error.Success()) { PlatformSP platform_sp = GetTarget().GetPlatform(); if (platform_sp && platform_sp->IsConnected()) SetUnixSignals(platform_sp->GetUnixSignals()); else SetUnixSignals(UnixSignals::Create(GetTarget().GetArchitecture())); } return error; } Status ProcessGDBRemote::WillLaunchOrAttach() { Status error; m_stdio_communication.Clear(); return error; } //---------------------------------------------------------------------- // Process Control //---------------------------------------------------------------------- Status ProcessGDBRemote::DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); Status error; if (log) log->Printf("ProcessGDBRemote::%s() entered", __FUNCTION__); uint32_t launch_flags = launch_info.GetFlags().Get(); FileSpec stdin_file_spec{}; FileSpec stdout_file_spec{}; FileSpec stderr_file_spec{}; FileSpec working_dir = launch_info.GetWorkingDirectory(); const FileAction *file_action; file_action = launch_info.GetFileActionForFD(STDIN_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) stdin_file_spec = file_action->GetFileSpec(); } file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) stdout_file_spec = file_action->GetFileSpec(); } file_action = launch_info.GetFileActionForFD(STDERR_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) stderr_file_spec = file_action->GetFileSpec(); } if (log) { if (stdin_file_spec || stdout_file_spec || stderr_file_spec) log->Printf("ProcessGDBRemote::%s provided with STDIO paths via " "launch_info: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, stdin_file_spec ? stdin_file_spec.GetCString() : "", stdout_file_spec ? stdout_file_spec.GetCString() : "", stderr_file_spec ? stderr_file_spec.GetCString() : ""); else log->Printf("ProcessGDBRemote::%s no STDIO paths given via launch_info", __FUNCTION__); } const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; if (stdin_file_spec || disable_stdio) { // the inferior will be reading stdin from the specified file // or stdio is completely disabled m_stdin_forward = false; } else { m_stdin_forward = true; } // ::LogSetBitMask (GDBR_LOG_DEFAULT); // ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | // LLDB_LOG_OPTION_PREPEND_TIMESTAMP | // LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); // ::LogSetLogFile ("/dev/stdout"); ObjectFile *object_file = exe_module->GetObjectFile(); if (object_file) { error = EstablishConnectionIfNeeded(launch_info); if (error.Success()) { lldb_utility::PseudoTerminal pty; const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; PlatformSP platform_sp(GetTarget().GetPlatform()); if (disable_stdio) { // set to /dev/null unless redirected to a file above if (!stdin_file_spec) stdin_file_spec.SetFile(FileSystem::DEV_NULL, false); if (!stdout_file_spec) stdout_file_spec.SetFile(FileSystem::DEV_NULL, false); if (!stderr_file_spec) stderr_file_spec.SetFile(FileSystem::DEV_NULL, false); } else if (platform_sp && platform_sp->IsHost()) { // If the debugserver is local and we aren't disabling STDIO, lets use // a pseudo terminal to instead of relying on the 'O' packets for stdio // since 'O' packets can really slow down debugging if the inferior // does a lot of output. if ((!stdin_file_spec || !stdout_file_spec || !stderr_file_spec) && pty.OpenFirstAvailableMaster(O_RDWR | O_NOCTTY, NULL, 0)) { FileSpec slave_name{pty.GetSlaveName(NULL, 0), false}; if (!stdin_file_spec) stdin_file_spec = slave_name; if (!stdout_file_spec) stdout_file_spec = slave_name; if (!stderr_file_spec) stderr_file_spec = slave_name; } if (log) log->Printf( "ProcessGDBRemote::%s adjusted STDIO paths for local platform " "(IsHost() is true) using slave: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, stdin_file_spec ? stdin_file_spec.GetCString() : "", stdout_file_spec ? stdout_file_spec.GetCString() : "", stderr_file_spec ? stderr_file_spec.GetCString() : ""); } if (log) log->Printf("ProcessGDBRemote::%s final STDIO paths after all " "adjustments: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, stdin_file_spec ? stdin_file_spec.GetCString() : "", stdout_file_spec ? stdout_file_spec.GetCString() : "", stderr_file_spec ? stderr_file_spec.GetCString() : ""); if (stdin_file_spec) m_gdb_comm.SetSTDIN(stdin_file_spec); if (stdout_file_spec) m_gdb_comm.SetSTDOUT(stdout_file_spec); if (stderr_file_spec) m_gdb_comm.SetSTDERR(stderr_file_spec); m_gdb_comm.SetDisableASLR(launch_flags & eLaunchFlagDisableASLR); m_gdb_comm.SetDetachOnError(launch_flags & eLaunchFlagDetachOnError); m_gdb_comm.SendLaunchArchPacket( GetTarget().GetArchitecture().GetArchitectureName()); const char *launch_event_data = launch_info.GetLaunchEventData(); if (launch_event_data != NULL && *launch_event_data != '\0') m_gdb_comm.SendLaunchEventDataPacket(launch_event_data); if (working_dir) { m_gdb_comm.SetWorkingDir(working_dir); } // Send the environment and the program + arguments after we connect const Args &environment = launch_info.GetEnvironmentEntries(); if (environment.GetArgumentCount()) { size_t num_environment_entries = environment.GetArgumentCount(); for (size_t i = 0; i < num_environment_entries; ++i) { const char *env_entry = environment.GetArgumentAtIndex(i); if (env_entry == NULL || m_gdb_comm.SendEnvironmentPacket(env_entry) != 0) break; } } { // Scope for the scoped timeout object GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm, std::chrono::seconds(10)); int arg_packet_err = m_gdb_comm.SendArgumentsPacket(launch_info); if (arg_packet_err == 0) { std::string error_str; if (m_gdb_comm.GetLaunchSuccess(error_str)) { SetID(m_gdb_comm.GetCurrentProcessID()); } else { error.SetErrorString(error_str.c_str()); } } else { error.SetErrorStringWithFormat("'A' packet returned an error: %i", arg_packet_err); } } if (GetID() == LLDB_INVALID_PROCESS_ID) { if (log) log->Printf("failed to connect to debugserver: %s", error.AsCString()); KillDebugserverProcess(); return error; } StringExtractorGDBRemote response; if (m_gdb_comm.GetStopReply(response)) { SetLastStopPacket(response); // '?' Packets must be handled differently in non-stop mode if (GetTarget().GetNonStopModeEnabled()) HandleStopReplySequence(); const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture(); if (process_arch.IsValid()) { GetTarget().MergeArchitecture(process_arch); } else { const ArchSpec &host_arch = m_gdb_comm.GetHostArchitecture(); if (host_arch.IsValid()) GetTarget().MergeArchitecture(host_arch); } SetPrivateState(SetThreadStopInfo(response)); if (!disable_stdio) { if (pty.GetMasterFileDescriptor() != lldb_utility::PseudoTerminal::invalid_fd) SetSTDIOFileDescriptor(pty.ReleaseMasterFileDescriptor()); } } } else { if (log) log->Printf("failed to connect to debugserver: %s", error.AsCString()); } } else { // Set our user ID to an invalid process ID. SetID(LLDB_INVALID_PROCESS_ID); error.SetErrorStringWithFormat( "failed to get object file from '%s' for arch %s", exe_module->GetFileSpec().GetFilename().AsCString(), exe_module->GetArchitecture().GetArchitectureName()); } return error; } Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) { Status error; // Only connect if we have a valid connect URL Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (!connect_url.empty()) { if (log) log->Printf("ProcessGDBRemote::%s Connecting to %s", __FUNCTION__, connect_url.str().c_str()); std::unique_ptr conn_ap( new ConnectionFileDescriptor()); if (conn_ap.get()) { const uint32_t max_retry_count = 50; uint32_t retry_count = 0; while (!m_gdb_comm.IsConnected()) { if (conn_ap->Connect(connect_url, &error) == eConnectionStatusSuccess) { m_gdb_comm.SetConnection(conn_ap.release()); break; } else if (error.WasInterrupted()) { // If we were interrupted, don't keep retrying. break; } retry_count++; if (retry_count >= max_retry_count) break; usleep(100000); } } } if (!m_gdb_comm.IsConnected()) { if (error.Success()) error.SetErrorString("not connected to remote gdb server"); return error; } // Start the communications read thread so all incoming data can be // parsed into packets and queued as they arrive. if (GetTarget().GetNonStopModeEnabled()) m_gdb_comm.StartReadThread(); // We always seem to be able to open a connection to a local port // so we need to make sure we can then send data to it. If we can't // then we aren't actually connected to anything, so try and do the // handshake with the remote GDB server and make sure that goes // alright. if (!m_gdb_comm.HandshakeWithServer(&error)) { m_gdb_comm.Disconnect(); if (error.Success()) error.SetErrorString("not connected to remote gdb server"); return error; } // Send $QNonStop:1 packet on startup if required if (GetTarget().GetNonStopModeEnabled()) GetTarget().SetNonStopModeEnabled(m_gdb_comm.SetNonStopMode(true)); m_gdb_comm.GetEchoSupported(); m_gdb_comm.GetThreadSuffixSupported(); m_gdb_comm.GetListThreadsInStopReplySupported(); m_gdb_comm.GetHostInfo(); m_gdb_comm.GetVContSupported('c'); m_gdb_comm.GetVAttachOrWaitSupported(); // Ask the remote server for the default thread id if (GetTarget().GetNonStopModeEnabled()) m_gdb_comm.GetDefaultThreadId(m_initial_tid); size_t num_cmds = GetExtraStartupCommands().GetArgumentCount(); for (size_t idx = 0; idx < num_cmds; idx++) { StringExtractorGDBRemote response; m_gdb_comm.SendPacketAndWaitForResponse( GetExtraStartupCommands().GetArgumentAtIndex(idx), response, false); } return error; } void ProcessGDBRemote::DidLaunchOrAttach(ArchSpec &process_arch) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::%s()", __FUNCTION__); if (GetID() != LLDB_INVALID_PROCESS_ID) { BuildDynamicRegisterInfo(false); // See if the GDB server supports the qHostInfo information // See if the GDB server supports the qProcessInfo packet, if so // prefer that over the Host information as it will be more specific // to our process. const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); if (remote_process_arch.IsValid()) { process_arch = remote_process_arch; if (log) log->Printf("ProcessGDBRemote::%s gdb-remote had process architecture, " "using %s %s", __FUNCTION__, process_arch.GetArchitectureName() ? process_arch.GetArchitectureName() : "", process_arch.GetTriple().getTriple().c_str() ? process_arch.GetTriple().getTriple().c_str() : ""); } else { process_arch = m_gdb_comm.GetHostArchitecture(); if (log) log->Printf("ProcessGDBRemote::%s gdb-remote did not have process " "architecture, using gdb-remote host architecture %s %s", __FUNCTION__, process_arch.GetArchitectureName() ? process_arch.GetArchitectureName() : "", process_arch.GetTriple().getTriple().c_str() ? process_arch.GetTriple().getTriple().c_str() : ""); } if (process_arch.IsValid()) { const ArchSpec &target_arch = GetTarget().GetArchitecture(); if (target_arch.IsValid()) { if (log) log->Printf( "ProcessGDBRemote::%s analyzing target arch, currently %s %s", __FUNCTION__, target_arch.GetArchitectureName() ? target_arch.GetArchitectureName() : "", target_arch.GetTriple().getTriple().c_str() ? target_arch.GetTriple().getTriple().c_str() : ""); // If the remote host is ARM and we have apple as the vendor, then // ARM executables and shared libraries can have mixed ARM // architectures. // You can have an armv6 executable, and if the host is armv7, then the // system will load the best possible architecture for all shared // libraries // it has, so we really need to take the remote host architecture as our // defacto architecture in this case. if ((process_arch.GetMachine() == llvm::Triple::arm || process_arch.GetMachine() == llvm::Triple::thumb) && process_arch.GetTriple().getVendor() == llvm::Triple::Apple) { GetTarget().SetArchitecture(process_arch); if (log) log->Printf("ProcessGDBRemote::%s remote process is ARM/Apple, " "setting target arch to %s %s", __FUNCTION__, process_arch.GetArchitectureName() ? process_arch.GetArchitectureName() : "", process_arch.GetTriple().getTriple().c_str() ? process_arch.GetTriple().getTriple().c_str() : ""); } else { // Fill in what is missing in the triple const llvm::Triple &remote_triple = process_arch.GetTriple(); llvm::Triple new_target_triple = target_arch.GetTriple(); if (new_target_triple.getVendorName().size() == 0) { new_target_triple.setVendor(remote_triple.getVendor()); if (new_target_triple.getOSName().size() == 0) { new_target_triple.setOS(remote_triple.getOS()); if (new_target_triple.getEnvironmentName().size() == 0) new_target_triple.setEnvironment( remote_triple.getEnvironment()); } ArchSpec new_target_arch = target_arch; new_target_arch.SetTriple(new_target_triple); GetTarget().SetArchitecture(new_target_arch); } } if (log) log->Printf("ProcessGDBRemote::%s final target arch after " "adjustments for remote architecture: %s %s", __FUNCTION__, target_arch.GetArchitectureName() ? target_arch.GetArchitectureName() : "", target_arch.GetTriple().getTriple().c_str() ? target_arch.GetTriple().getTriple().c_str() : ""); } else { // The target doesn't have a valid architecture yet, set it from // the architecture we got from the remote GDB server GetTarget().SetArchitecture(process_arch); } } // Find out which StructuredDataPlugins are supported by the // debug monitor. These plugins transmit data over async $J packets. auto supported_packets_array = m_gdb_comm.GetSupportedStructuredDataPlugins(); if (supported_packets_array) MapSupportedStructuredDataPlugins(*supported_packets_array); } } void ProcessGDBRemote::DidLaunch() { ArchSpec process_arch; DidLaunchOrAttach(process_arch); } Status ProcessGDBRemote::DoAttachToProcessWithID( lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); Status error; if (log) log->Printf("ProcessGDBRemote::%s()", __FUNCTION__); // Clear out and clean up from any current state Clear(); if (attach_pid != LLDB_INVALID_PROCESS_ID) { error = EstablishConnectionIfNeeded(attach_info); if (error.Success()) { m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid); SetID(attach_pid); m_async_broadcaster.BroadcastEvent( eBroadcastBitAsyncContinue, new EventDataBytes(packet, packet_len)); } else SetExitStatus(-1, error.AsCString()); } return error; } Status ProcessGDBRemote::DoAttachToProcessWithName( const char *process_name, const ProcessAttachInfo &attach_info) { Status error; // Clear out and clean up from any current state Clear(); if (process_name && process_name[0]) { error = EstablishConnectionIfNeeded(attach_info); if (error.Success()) { StreamString packet; m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); if (attach_info.GetWaitForLaunch()) { if (!m_gdb_comm.GetVAttachOrWaitSupported()) { packet.PutCString("vAttachWait"); } else { if (attach_info.GetIgnoreExisting()) packet.PutCString("vAttachWait"); else packet.PutCString("vAttachOrWait"); } } else packet.PutCString("vAttachName"); packet.PutChar(';'); packet.PutBytesAsRawHex8(process_name, strlen(process_name), endian::InlHostByteOrder(), endian::InlHostByteOrder()); m_async_broadcaster.BroadcastEvent( eBroadcastBitAsyncContinue, new EventDataBytes(packet.GetString().data(), packet.GetSize())); } else SetExitStatus(-1, error.AsCString()); } return error; } +lldb::user_id_t ProcessGDBRemote::StartTrace(const TraceOptions &options, + Status &error) { + return m_gdb_comm.SendStartTracePacket(options, error); +} + +Status ProcessGDBRemote::StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) { + return m_gdb_comm.SendStopTracePacket(uid, thread_id); +} + +Status ProcessGDBRemote::GetData(lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, + size_t offset) { + return m_gdb_comm.SendGetDataPacket(uid, thread_id, buffer, offset); +} + +Status ProcessGDBRemote::GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, + size_t offset) { + return m_gdb_comm.SendGetMetaDataPacket(uid, thread_id, buffer, offset); +} + +Status ProcessGDBRemote::GetTraceConfig(lldb::user_id_t uid, + TraceOptions &options) { + return m_gdb_comm.SendGetTraceConfigPacket(uid, options); +} + void ProcessGDBRemote::DidExit() { // When we exit, disconnect from the GDB server communications m_gdb_comm.Disconnect(); } void ProcessGDBRemote::DidAttach(ArchSpec &process_arch) { // If you can figure out what the architecture is, fill it in here. process_arch.Clear(); DidLaunchOrAttach(process_arch); } Status ProcessGDBRemote::WillResume() { m_continue_c_tids.clear(); m_continue_C_tids.clear(); m_continue_s_tids.clear(); m_continue_S_tids.clear(); m_jstopinfo_sp.reset(); m_jthreadsinfo_sp.reset(); return Status(); } Status ProcessGDBRemote::DoResume() { Status error; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::Resume()"); ListenerSP listener_sp( Listener::MakeListener("gdb-remote.resume-packet-sent")); if (listener_sp->StartListeningForEvents( &m_gdb_comm, GDBRemoteCommunication::eBroadcastBitRunPacketSent)) { listener_sp->StartListeningForEvents( &m_async_broadcaster, ProcessGDBRemote::eBroadcastBitAsyncThreadDidExit); const size_t num_threads = GetThreadList().GetSize(); StreamString continue_packet; bool continue_packet_error = false; if (m_gdb_comm.HasAnyVContSupport()) { if (!GetTarget().GetNonStopModeEnabled() && (m_continue_c_tids.size() == num_threads || (m_continue_c_tids.empty() && m_continue_C_tids.empty() && m_continue_s_tids.empty() && m_continue_S_tids.empty()))) { // All threads are continuing, just send a "c" packet continue_packet.PutCString("c"); } else { continue_packet.PutCString("vCont"); if (!m_continue_c_tids.empty()) { if (m_gdb_comm.GetVContSupported('c')) { for (tid_collection::const_iterator t_pos = m_continue_c_tids.begin(), t_end = m_continue_c_tids.end(); t_pos != t_end; ++t_pos) continue_packet.Printf(";c:%4.4" PRIx64, *t_pos); } else continue_packet_error = true; } if (!continue_packet_error && !m_continue_C_tids.empty()) { if (m_gdb_comm.GetVContSupported('C')) { for (tid_sig_collection::const_iterator s_pos = m_continue_C_tids.begin(), s_end = m_continue_C_tids.end(); s_pos != s_end; ++s_pos) continue_packet.Printf(";C%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first); } else continue_packet_error = true; } if (!continue_packet_error && !m_continue_s_tids.empty()) { if (m_gdb_comm.GetVContSupported('s')) { for (tid_collection::const_iterator t_pos = m_continue_s_tids.begin(), t_end = m_continue_s_tids.end(); t_pos != t_end; ++t_pos) continue_packet.Printf(";s:%4.4" PRIx64, *t_pos); } else continue_packet_error = true; } if (!continue_packet_error && !m_continue_S_tids.empty()) { if (m_gdb_comm.GetVContSupported('S')) { for (tid_sig_collection::const_iterator s_pos = m_continue_S_tids.begin(), s_end = m_continue_S_tids.end(); s_pos != s_end; ++s_pos) continue_packet.Printf(";S%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first); } else continue_packet_error = true; } if (continue_packet_error) continue_packet.Clear(); } } else continue_packet_error = true; if (continue_packet_error) { // Either no vCont support, or we tried to use part of the vCont // packet that wasn't supported by the remote GDB server. // We need to try and make a simple packet that can do our continue const size_t num_continue_c_tids = m_continue_c_tids.size(); const size_t num_continue_C_tids = m_continue_C_tids.size(); const size_t num_continue_s_tids = m_continue_s_tids.size(); const size_t num_continue_S_tids = m_continue_S_tids.size(); if (num_continue_c_tids > 0) { if (num_continue_c_tids == num_threads) { // All threads are resuming... m_gdb_comm.SetCurrentThreadForRun(-1); continue_packet.PutChar('c'); continue_packet_error = false; } else if (num_continue_c_tids == 1 && num_continue_C_tids == 0 && num_continue_s_tids == 0 && num_continue_S_tids == 0) { // Only one thread is continuing m_gdb_comm.SetCurrentThreadForRun(m_continue_c_tids.front()); continue_packet.PutChar('c'); continue_packet_error = false; } } if (continue_packet_error && num_continue_C_tids > 0) { if ((num_continue_C_tids + num_continue_c_tids) == num_threads && num_continue_C_tids > 0 && num_continue_s_tids == 0 && num_continue_S_tids == 0) { const int continue_signo = m_continue_C_tids.front().second; // Only one thread is continuing if (num_continue_C_tids > 1) { // More that one thread with a signal, yet we don't have // vCont support and we are being asked to resume each // thread with a signal, we need to make sure they are // all the same signal, or we can't issue the continue // accurately with the current support... if (num_continue_C_tids > 1) { continue_packet_error = false; for (size_t i = 1; i < m_continue_C_tids.size(); ++i) { if (m_continue_C_tids[i].second != continue_signo) continue_packet_error = true; } } if (!continue_packet_error) m_gdb_comm.SetCurrentThreadForRun(-1); } else { // Set the continue thread ID continue_packet_error = false; m_gdb_comm.SetCurrentThreadForRun(m_continue_C_tids.front().first); } if (!continue_packet_error) { // Add threads continuing with the same signo... continue_packet.Printf("C%2.2x", continue_signo); } } } if (continue_packet_error && num_continue_s_tids > 0) { if (num_continue_s_tids == num_threads) { // All threads are resuming... m_gdb_comm.SetCurrentThreadForRun(-1); // If in Non-Stop-Mode use vCont when stepping if (GetTarget().GetNonStopModeEnabled()) { if (m_gdb_comm.GetVContSupported('s')) continue_packet.PutCString("vCont;s"); else continue_packet.PutChar('s'); } else continue_packet.PutChar('s'); continue_packet_error = false; } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 && num_continue_s_tids == 1 && num_continue_S_tids == 0) { // Only one thread is stepping m_gdb_comm.SetCurrentThreadForRun(m_continue_s_tids.front()); continue_packet.PutChar('s'); continue_packet_error = false; } } if (!continue_packet_error && num_continue_S_tids > 0) { if (num_continue_S_tids == num_threads) { const int step_signo = m_continue_S_tids.front().second; // Are all threads trying to step with the same signal? continue_packet_error = false; if (num_continue_S_tids > 1) { for (size_t i = 1; i < num_threads; ++i) { if (m_continue_S_tids[i].second != step_signo) continue_packet_error = true; } } if (!continue_packet_error) { // Add threads stepping with the same signo... m_gdb_comm.SetCurrentThreadForRun(-1); continue_packet.Printf("S%2.2x", step_signo); } } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 && num_continue_s_tids == 0 && num_continue_S_tids == 1) { // Only one thread is stepping with signal m_gdb_comm.SetCurrentThreadForRun(m_continue_S_tids.front().first); continue_packet.Printf("S%2.2x", m_continue_S_tids.front().second); continue_packet_error = false; } } } if (continue_packet_error) { error.SetErrorString("can't make continue packet for this resume"); } else { EventSP event_sp; if (!m_async_thread.IsJoinable()) { error.SetErrorString("Trying to resume but the async thread is dead."); if (log) log->Printf("ProcessGDBRemote::DoResume: Trying to resume but the " "async thread is dead."); return error; } m_async_broadcaster.BroadcastEvent( eBroadcastBitAsyncContinue, new EventDataBytes(continue_packet.GetString().data(), continue_packet.GetSize())); if (listener_sp->GetEvent(event_sp, std::chrono::seconds(5)) == false) { error.SetErrorString("Resume timed out."); if (log) log->Printf("ProcessGDBRemote::DoResume: Resume timed out."); } else if (event_sp->BroadcasterIs(&m_async_broadcaster)) { error.SetErrorString("Broadcast continue, but the async thread was " "killed before we got an ack back."); if (log) log->Printf("ProcessGDBRemote::DoResume: Broadcast continue, but the " "async thread was killed before we got an ack back."); return error; } } } return error; } void ProcessGDBRemote::HandleStopReplySequence() { while (true) { // Send vStopped StringExtractorGDBRemote response; m_gdb_comm.SendPacketAndWaitForResponse("vStopped", response, false); // OK represents end of signal list if (response.IsOKResponse()) break; // If not OK or a normal packet we have a problem if (!response.IsNormalResponse()) break; SetLastStopPacket(response); } } void ProcessGDBRemote::ClearThreadIDList() { std::lock_guard guard(m_thread_list_real.GetMutex()); m_thread_ids.clear(); m_thread_pcs.clear(); } size_t ProcessGDBRemote::UpdateThreadIDsFromStopReplyThreadsValue(std::string &value) { m_thread_ids.clear(); m_thread_pcs.clear(); size_t comma_pos; lldb::tid_t tid; while ((comma_pos = value.find(',')) != std::string::npos) { value[comma_pos] = '\0'; // thread in big endian hex tid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_THREAD_ID, 16); if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back(tid); value.erase(0, comma_pos + 1); } tid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_THREAD_ID, 16); if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back(tid); return m_thread_ids.size(); } size_t ProcessGDBRemote::UpdateThreadPCsFromStopReplyThreadsValue(std::string &value) { m_thread_pcs.clear(); size_t comma_pos; lldb::addr_t pc; while ((comma_pos = value.find(',')) != std::string::npos) { value[comma_pos] = '\0'; pc = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16); if (pc != LLDB_INVALID_ADDRESS) m_thread_pcs.push_back(pc); value.erase(0, comma_pos + 1); } pc = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16); if (pc != LLDB_INVALID_THREAD_ID) m_thread_pcs.push_back(pc); return m_thread_pcs.size(); } bool ProcessGDBRemote::UpdateThreadIDList() { std::lock_guard guard(m_thread_list_real.GetMutex()); if (m_jthreadsinfo_sp) { // If we have the JSON threads info, we can get the thread list from that StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray(); if (thread_infos && thread_infos->GetSize() > 0) { m_thread_ids.clear(); m_thread_pcs.clear(); thread_infos->ForEach([this](StructuredData::Object *object) -> bool { StructuredData::Dictionary *thread_dict = object->GetAsDictionary(); if (thread_dict) { // Set the thread stop info from the JSON dictionary SetThreadStopInfo(thread_dict); lldb::tid_t tid = LLDB_INVALID_THREAD_ID; if (thread_dict->GetValueForKeyAsInteger("tid", tid)) m_thread_ids.push_back(tid); } return true; // Keep iterating through all thread_info objects }); } if (!m_thread_ids.empty()) return true; } else { // See if we can get the thread IDs from the current stop reply packets // that might contain a "threads" key/value pair // Lock the thread stack while we access it // Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); std::unique_lock stop_stack_lock( m_last_stop_packet_mutex, std::defer_lock); if (stop_stack_lock.try_lock()) { // Get the number of stop packets on the stack int nItems = m_stop_packet_stack.size(); // Iterate over them for (int i = 0; i < nItems; i++) { // Get the thread stop info StringExtractorGDBRemote &stop_info = m_stop_packet_stack[i]; const std::string &stop_info_str = stop_info.GetStringRef(); m_thread_pcs.clear(); const size_t thread_pcs_pos = stop_info_str.find(";thread-pcs:"); if (thread_pcs_pos != std::string::npos) { const size_t start = thread_pcs_pos + strlen(";thread-pcs:"); const size_t end = stop_info_str.find(';', start); if (end != std::string::npos) { std::string value = stop_info_str.substr(start, end - start); UpdateThreadPCsFromStopReplyThreadsValue(value); } } const size_t threads_pos = stop_info_str.find(";threads:"); if (threads_pos != std::string::npos) { const size_t start = threads_pos + strlen(";threads:"); const size_t end = stop_info_str.find(';', start); if (end != std::string::npos) { std::string value = stop_info_str.substr(start, end - start); if (UpdateThreadIDsFromStopReplyThreadsValue(value)) return true; } } } } } bool sequence_mutex_unavailable = false; m_gdb_comm.GetCurrentThreadIDs(m_thread_ids, sequence_mutex_unavailable); if (sequence_mutex_unavailable) { return false; // We just didn't get the list } return true; } bool ProcessGDBRemote::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) { // locker will keep a mutex locked until it goes out of scope Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_THREAD)); LLDB_LOGV(log, "pid = {0}", GetID()); size_t num_thread_ids = m_thread_ids.size(); // The "m_thread_ids" thread ID list should always be updated after each stop // reply packet, but in case it isn't, update it here. if (num_thread_ids == 0) { if (!UpdateThreadIDList()) return false; num_thread_ids = m_thread_ids.size(); } ThreadList old_thread_list_copy(old_thread_list); if (num_thread_ids > 0) { for (size_t i = 0; i < num_thread_ids; ++i) { tid_t tid = m_thread_ids[i]; ThreadSP thread_sp( old_thread_list_copy.RemoveThreadByProtocolID(tid, false)); if (!thread_sp) { thread_sp.reset(new ThreadGDBRemote(*this, tid)); LLDB_LOGV(log, "Making new thread: {0} for thread ID: {1:x}.", thread_sp.get(), thread_sp->GetID()); } else { LLDB_LOGV(log, "Found old thread: {0} for thread ID: {1:x}.", thread_sp.get(), thread_sp->GetID()); } SetThreadPc(thread_sp, i); new_thread_list.AddThreadSortedByIndexID(thread_sp); } } // Whatever that is left in old_thread_list_copy are not // present in new_thread_list. Remove non-existent threads from internal id // table. size_t old_num_thread_ids = old_thread_list_copy.GetSize(false); for (size_t i = 0; i < old_num_thread_ids; i++) { ThreadSP old_thread_sp(old_thread_list_copy.GetThreadAtIndex(i, false)); if (old_thread_sp) { lldb::tid_t old_thread_id = old_thread_sp->GetProtocolID(); m_thread_id_to_index_id_map.erase(old_thread_id); } } return true; } void ProcessGDBRemote::SetThreadPc(const ThreadSP &thread_sp, uint64_t index) { if (m_thread_ids.size() == m_thread_pcs.size() && thread_sp.get() && GetByteOrder() != eByteOrderInvalid) { ThreadGDBRemote *gdb_thread = static_cast(thread_sp.get()); RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext()); if (reg_ctx_sp) { uint32_t pc_regnum = reg_ctx_sp->ConvertRegisterKindToRegisterNumber( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); if (pc_regnum != LLDB_INVALID_REGNUM) { gdb_thread->PrivateSetRegisterValue(pc_regnum, m_thread_pcs[index]); } } } } bool ProcessGDBRemote::GetThreadStopInfoFromJSON( ThreadGDBRemote *thread, const StructuredData::ObjectSP &thread_infos_sp) { // See if we got thread stop infos for all threads via the "jThreadsInfo" // packet if (thread_infos_sp) { StructuredData::Array *thread_infos = thread_infos_sp->GetAsArray(); if (thread_infos) { lldb::tid_t tid; const size_t n = thread_infos->GetSize(); for (size_t i = 0; i < n; ++i) { StructuredData::Dictionary *thread_dict = thread_infos->GetItemAtIndex(i)->GetAsDictionary(); if (thread_dict) { if (thread_dict->GetValueForKeyAsInteger( "tid", tid, LLDB_INVALID_THREAD_ID)) { if (tid == thread->GetID()) return (bool)SetThreadStopInfo(thread_dict); } } } } } return false; } bool ProcessGDBRemote::CalculateThreadStopInfo(ThreadGDBRemote *thread) { // See if we got thread stop infos for all threads via the "jThreadsInfo" // packet if (GetThreadStopInfoFromJSON(thread, m_jthreadsinfo_sp)) return true; // See if we got thread stop info for any threads valid stop info reasons // threads // via the "jstopinfo" packet stop reply packet key/value pair? if (m_jstopinfo_sp) { // If we have "jstopinfo" then we have stop descriptions for all threads // that have stop reasons, and if there is no entry for a thread, then // it has no stop reason. thread->GetRegisterContext()->InvalidateIfNeeded(true); if (!GetThreadStopInfoFromJSON(thread, m_jstopinfo_sp)) { thread->SetStopInfo(StopInfoSP()); } return true; } // Fall back to using the qThreadStopInfo packet StringExtractorGDBRemote stop_packet; if (GetGDBRemote().GetThreadStopInfo(thread->GetProtocolID(), stop_packet)) return SetThreadStopInfo(stop_packet) == eStateStopped; return false; } ThreadSP ProcessGDBRemote::SetThreadStopInfo( lldb::tid_t tid, ExpeditedRegisterMap &expedited_register_map, uint8_t signo, const std::string &thread_name, const std::string &reason, const std::string &description, uint32_t exc_type, const std::vector &exc_data, addr_t thread_dispatch_qaddr, bool queue_vars_valid, // Set to true if queue_name, queue_kind and // queue_serial are valid LazyBool associated_with_dispatch_queue, addr_t dispatch_queue_t, std::string &queue_name, QueueKind queue_kind, uint64_t queue_serial) { ThreadSP thread_sp; if (tid != LLDB_INVALID_THREAD_ID) { // Scope for "locker" below { // m_thread_list_real does have its own mutex, but we need to // hold onto the mutex between the call to // m_thread_list_real.FindThreadByID(...) // and the m_thread_list_real.AddThread(...) so it doesn't change on us std::lock_guard guard( m_thread_list_real.GetMutex()); thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false); if (!thread_sp) { // Create the thread if we need to thread_sp.reset(new ThreadGDBRemote(*this, tid)); m_thread_list_real.AddThread(thread_sp); } } if (thread_sp) { ThreadGDBRemote *gdb_thread = static_cast(thread_sp.get()); gdb_thread->GetRegisterContext()->InvalidateIfNeeded(true); auto iter = std::find(m_thread_ids.begin(), m_thread_ids.end(), tid); if (iter != m_thread_ids.end()) { SetThreadPc(thread_sp, iter - m_thread_ids.begin()); } for (const auto &pair : expedited_register_map) { StringExtractor reg_value_extractor; reg_value_extractor.GetStringRef() = pair.second; DataBufferSP buffer_sp(new DataBufferHeap( reg_value_extractor.GetStringRef().size() / 2, 0)); reg_value_extractor.GetHexBytes(buffer_sp->GetData(), '\xcc'); gdb_thread->PrivateSetRegisterValue(pair.first, buffer_sp->GetData()); } thread_sp->SetName(thread_name.empty() ? NULL : thread_name.c_str()); gdb_thread->SetThreadDispatchQAddr(thread_dispatch_qaddr); // Check if the GDB server was able to provide the queue name, kind and // serial number if (queue_vars_valid) gdb_thread->SetQueueInfo(std::move(queue_name), queue_kind, queue_serial, dispatch_queue_t, associated_with_dispatch_queue); else gdb_thread->ClearQueueInfo(); gdb_thread->SetAssociatedWithLibdispatchQueue( associated_with_dispatch_queue); if (dispatch_queue_t != LLDB_INVALID_ADDRESS) gdb_thread->SetQueueLibdispatchQueueAddress(dispatch_queue_t); // Make sure we update our thread stop reason just once if (!thread_sp->StopInfoIsUpToDate()) { thread_sp->SetStopInfo(StopInfoSP()); // If there's a memory thread backed by this thread, we need to use it // to calcualte StopInfo. ThreadSP memory_thread_sp = m_thread_list.FindThreadByProtocolID(thread_sp->GetProtocolID()); if (memory_thread_sp) thread_sp = memory_thread_sp; if (exc_type != 0) { const size_t exc_data_size = exc_data.size(); thread_sp->SetStopInfo( StopInfoMachException::CreateStopReasonWithMachException( *thread_sp, exc_type, exc_data_size, exc_data_size >= 1 ? exc_data[0] : 0, exc_data_size >= 2 ? exc_data[1] : 0, exc_data_size >= 3 ? exc_data[2] : 0)); } else { bool handled = false; bool did_exec = false; if (!reason.empty()) { if (reason.compare("trace") == 0) { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess() ->GetBreakpointSiteList() .FindByAddress(pc); // If the current pc is a breakpoint site then the StopInfo should // be set to Breakpoint // Otherwise, it will be set to Trace. if (bp_site_sp && bp_site_sp->ValidForThisThread(thread_sp.get())) { thread_sp->SetStopInfo( StopInfo::CreateStopReasonWithBreakpointSiteID( *thread_sp, bp_site_sp->GetID())); } else thread_sp->SetStopInfo( StopInfo::CreateStopReasonToTrace(*thread_sp)); handled = true; } else if (reason.compare("breakpoint") == 0) { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess() ->GetBreakpointSiteList() .FindByAddress(pc); if (bp_site_sp) { // If the breakpoint is for this thread, then we'll report the // hit, but if it is for another thread, // we can just report no reason. We don't need to worry about // stepping over the breakpoint here, that // will be taken care of when the thread resumes and notices // that there's a breakpoint under the pc. handled = true; if (bp_site_sp->ValidForThisThread(thread_sp.get())) { thread_sp->SetStopInfo( StopInfo::CreateStopReasonWithBreakpointSiteID( *thread_sp, bp_site_sp->GetID())); } else { StopInfoSP invalid_stop_info_sp; thread_sp->SetStopInfo(invalid_stop_info_sp); } } } else if (reason.compare("trap") == 0) { // Let the trap just use the standard signal stop reason below... } else if (reason.compare("watchpoint") == 0) { StringExtractor desc_extractor(description.c_str()); addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32); addr_t wp_hit_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); watch_id_t watch_id = LLDB_INVALID_WATCH_ID; if (wp_addr != LLDB_INVALID_ADDRESS) { WatchpointSP wp_sp; ArchSpec::Core core = GetTarget().GetArchitecture().GetCore(); if ((core >= ArchSpec::kCore_mips_first && core <= ArchSpec::kCore_mips_last) || (core >= ArchSpec::eCore_arm_generic && core <= ArchSpec::eCore_arm_aarch64)) wp_sp = GetTarget().GetWatchpointList().FindByAddress( wp_hit_addr); if (!wp_sp) wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr); if (wp_sp) { wp_sp->SetHardwareIndex(wp_index); watch_id = wp_sp->GetID(); } } if (watch_id == LLDB_INVALID_WATCH_ID) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet( GDBR_LOG_WATCHPOINTS)); if (log) log->Printf("failed to find watchpoint"); } thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithWatchpointID( *thread_sp, watch_id, wp_hit_addr)); handled = true; } else if (reason.compare("exception") == 0) { thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithException( *thread_sp, description.c_str())); handled = true; } else if (reason.compare("exec") == 0) { did_exec = true; thread_sp->SetStopInfo( StopInfo::CreateStopReasonWithExec(*thread_sp)); handled = true; } } else if (!signo) { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress( pc); // If the current pc is a breakpoint site then the StopInfo should // be set to Breakpoint // even though the remote stub did not set it as such. This can // happen when // the thread is involuntarily interrupted (e.g. due to stops on // other // threads) just as it is about to execute the breakpoint // instruction. if (bp_site_sp && bp_site_sp->ValidForThisThread(thread_sp.get())) { thread_sp->SetStopInfo( StopInfo::CreateStopReasonWithBreakpointSiteID( *thread_sp, bp_site_sp->GetID())); handled = true; } } if (!handled && signo && did_exec == false) { if (signo == SIGTRAP) { // Currently we are going to assume SIGTRAP means we are either // hitting a breakpoint or hardware single stepping. handled = true; addr_t pc = thread_sp->GetRegisterContext()->GetPC() + m_breakpoint_pc_offset; lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess() ->GetBreakpointSiteList() .FindByAddress(pc); if (bp_site_sp) { // If the breakpoint is for this thread, then we'll report the // hit, but if it is for another thread, // we can just report no reason. We don't need to worry about // stepping over the breakpoint here, that // will be taken care of when the thread resumes and notices // that there's a breakpoint under the pc. if (bp_site_sp->ValidForThisThread(thread_sp.get())) { if (m_breakpoint_pc_offset != 0) thread_sp->GetRegisterContext()->SetPC(pc); thread_sp->SetStopInfo( StopInfo::CreateStopReasonWithBreakpointSiteID( *thread_sp, bp_site_sp->GetID())); } else { StopInfoSP invalid_stop_info_sp; thread_sp->SetStopInfo(invalid_stop_info_sp); } } else { // If we were stepping then assume the stop was the result of // the trace. If we were // not stepping then report the SIGTRAP. // FIXME: We are still missing the case where we single step // over a trap instruction. if (thread_sp->GetTemporaryResumeState() == eStateStepping) thread_sp->SetStopInfo( StopInfo::CreateStopReasonToTrace(*thread_sp)); else thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithSignal( *thread_sp, signo, description.c_str())); } } if (!handled) thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithSignal( *thread_sp, signo, description.c_str())); } if (!description.empty()) { lldb::StopInfoSP stop_info_sp(thread_sp->GetStopInfo()); if (stop_info_sp) { const char *stop_info_desc = stop_info_sp->GetDescription(); if (!stop_info_desc || !stop_info_desc[0]) stop_info_sp->SetDescription(description.c_str()); } else { thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithException( *thread_sp, description.c_str())); } } } } } } return thread_sp; } lldb::ThreadSP ProcessGDBRemote::SetThreadStopInfo(StructuredData::Dictionary *thread_dict) { static ConstString g_key_tid("tid"); static ConstString g_key_name("name"); static ConstString g_key_reason("reason"); static ConstString g_key_metype("metype"); static ConstString g_key_medata("medata"); static ConstString g_key_qaddr("qaddr"); static ConstString g_key_dispatch_queue_t("dispatch_queue_t"); static ConstString g_key_associated_with_dispatch_queue( "associated_with_dispatch_queue"); static ConstString g_key_queue_name("qname"); static ConstString g_key_queue_kind("qkind"); static ConstString g_key_queue_serial_number("qserialnum"); static ConstString g_key_registers("registers"); static ConstString g_key_memory("memory"); static ConstString g_key_address("address"); static ConstString g_key_bytes("bytes"); static ConstString g_key_description("description"); static ConstString g_key_signal("signal"); // Stop with signal and thread info lldb::tid_t tid = LLDB_INVALID_THREAD_ID; uint8_t signo = 0; std::string value; std::string thread_name; std::string reason; std::string description; uint32_t exc_type = 0; std::vector exc_data; addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; ExpeditedRegisterMap expedited_register_map; bool queue_vars_valid = false; addr_t dispatch_queue_t = LLDB_INVALID_ADDRESS; LazyBool associated_with_dispatch_queue = eLazyBoolCalculate; std::string queue_name; QueueKind queue_kind = eQueueKindUnknown; uint64_t queue_serial_number = 0; // Iterate through all of the thread dictionary key/value pairs from the // structured data dictionary thread_dict->ForEach([this, &tid, &expedited_register_map, &thread_name, &signo, &reason, &description, &exc_type, &exc_data, &thread_dispatch_qaddr, &queue_vars_valid, &associated_with_dispatch_queue, &dispatch_queue_t, &queue_name, &queue_kind, &queue_serial_number]( ConstString key, StructuredData::Object *object) -> bool { if (key == g_key_tid) { // thread in big endian hex tid = object->GetIntegerValue(LLDB_INVALID_THREAD_ID); } else if (key == g_key_metype) { // exception type in big endian hex exc_type = object->GetIntegerValue(0); } else if (key == g_key_medata) { // exception data in big endian hex StructuredData::Array *array = object->GetAsArray(); if (array) { array->ForEach([&exc_data](StructuredData::Object *object) -> bool { exc_data.push_back(object->GetIntegerValue()); return true; // Keep iterating through all array items }); } } else if (key == g_key_name) { thread_name = object->GetStringValue(); } else if (key == g_key_qaddr) { thread_dispatch_qaddr = object->GetIntegerValue(LLDB_INVALID_ADDRESS); } else if (key == g_key_queue_name) { queue_vars_valid = true; queue_name = object->GetStringValue(); } else if (key == g_key_queue_kind) { std::string queue_kind_str = object->GetStringValue(); if (queue_kind_str == "serial") { queue_vars_valid = true; queue_kind = eQueueKindSerial; } else if (queue_kind_str == "concurrent") { queue_vars_valid = true; queue_kind = eQueueKindConcurrent; } } else if (key == g_key_queue_serial_number) { queue_serial_number = object->GetIntegerValue(0); if (queue_serial_number != 0) queue_vars_valid = true; } else if (key == g_key_dispatch_queue_t) { dispatch_queue_t = object->GetIntegerValue(0); if (dispatch_queue_t != 0 && dispatch_queue_t != LLDB_INVALID_ADDRESS) queue_vars_valid = true; } else if (key == g_key_associated_with_dispatch_queue) { queue_vars_valid = true; bool associated = object->GetBooleanValue(); if (associated) associated_with_dispatch_queue = eLazyBoolYes; else associated_with_dispatch_queue = eLazyBoolNo; } else if (key == g_key_reason) { reason = object->GetStringValue(); } else if (key == g_key_description) { description = object->GetStringValue(); } else if (key == g_key_registers) { StructuredData::Dictionary *registers_dict = object->GetAsDictionary(); if (registers_dict) { registers_dict->ForEach( [&expedited_register_map](ConstString key, StructuredData::Object *object) -> bool { const uint32_t reg = StringConvert::ToUInt32(key.GetCString(), UINT32_MAX, 10); if (reg != UINT32_MAX) expedited_register_map[reg] = object->GetStringValue(); return true; // Keep iterating through all array items }); } } else if (key == g_key_memory) { StructuredData::Array *array = object->GetAsArray(); if (array) { array->ForEach([this](StructuredData::Object *object) -> bool { StructuredData::Dictionary *mem_cache_dict = object->GetAsDictionary(); if (mem_cache_dict) { lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS; if (mem_cache_dict->GetValueForKeyAsInteger( "address", mem_cache_addr)) { if (mem_cache_addr != LLDB_INVALID_ADDRESS) { llvm::StringRef str; if (mem_cache_dict->GetValueForKeyAsString("bytes", str)) { StringExtractor bytes(str); bytes.SetFilePos(0); const size_t byte_size = bytes.GetStringRef().size() / 2; DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0)); const size_t bytes_copied = bytes.GetHexBytes(data_buffer_sp->GetData(), 0); if (bytes_copied == byte_size) m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp); } } } } return true; // Keep iterating through all array items }); } } else if (key == g_key_signal) signo = object->GetIntegerValue(LLDB_INVALID_SIGNAL_NUMBER); return true; // Keep iterating through all dictionary key/value pairs }); return SetThreadStopInfo(tid, expedited_register_map, signo, thread_name, reason, description, exc_type, exc_data, thread_dispatch_qaddr, queue_vars_valid, associated_with_dispatch_queue, dispatch_queue_t, queue_name, queue_kind, queue_serial_number); } StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) { stop_packet.SetFilePos(0); const char stop_type = stop_packet.GetChar(); switch (stop_type) { case 'T': case 'S': { // This is a bit of a hack, but is is required. If we did exec, we // need to clear our thread lists and also know to rebuild our dynamic // register info before we lookup and threads and populate the expedited // register values so we need to know this right away so we can cleanup // and update our registers. const uint32_t stop_id = GetStopID(); if (stop_id == 0) { // Our first stop, make sure we have a process ID, and also make // sure we know about our registers if (GetID() == LLDB_INVALID_PROCESS_ID) { lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); if (pid != LLDB_INVALID_PROCESS_ID) SetID(pid); } BuildDynamicRegisterInfo(true); } // Stop with signal and thread info lldb::tid_t tid = LLDB_INVALID_THREAD_ID; const uint8_t signo = stop_packet.GetHexU8(); llvm::StringRef key; llvm::StringRef value; std::string thread_name; std::string reason; std::string description; uint32_t exc_type = 0; std::vector exc_data; addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; bool queue_vars_valid = false; // says if locals below that start with "queue_" are valid addr_t dispatch_queue_t = LLDB_INVALID_ADDRESS; LazyBool associated_with_dispatch_queue = eLazyBoolCalculate; std::string queue_name; QueueKind queue_kind = eQueueKindUnknown; uint64_t queue_serial_number = 0; ExpeditedRegisterMap expedited_register_map; while (stop_packet.GetNameColonValue(key, value)) { if (key.compare("metype") == 0) { // exception type in big endian hex value.getAsInteger(16, exc_type); } else if (key.compare("medata") == 0) { // exception data in big endian hex uint64_t x; value.getAsInteger(16, x); exc_data.push_back(x); } else if (key.compare("thread") == 0) { // thread in big endian hex if (value.getAsInteger(16, tid)) tid = LLDB_INVALID_THREAD_ID; } else if (key.compare("threads") == 0) { std::lock_guard guard( m_thread_list_real.GetMutex()); m_thread_ids.clear(); // A comma separated list of all threads in the current // process that includes the thread for this stop reply // packet lldb::tid_t tid; while (!value.empty()) { llvm::StringRef tid_str; std::tie(tid_str, value) = value.split(','); if (tid_str.getAsInteger(16, tid)) tid = LLDB_INVALID_THREAD_ID; m_thread_ids.push_back(tid); } } else if (key.compare("thread-pcs") == 0) { m_thread_pcs.clear(); // A comma separated list of all threads in the current // process that includes the thread for this stop reply // packet lldb::addr_t pc; while (!value.empty()) { llvm::StringRef pc_str; std::tie(pc_str, value) = value.split(','); if (pc_str.getAsInteger(16, pc)) pc = LLDB_INVALID_ADDRESS; m_thread_pcs.push_back(pc); } } else if (key.compare("jstopinfo") == 0) { StringExtractor json_extractor(value); std::string json; // Now convert the HEX bytes into a string value json_extractor.GetHexByteString(json); // This JSON contains thread IDs and thread stop info for all threads. // It doesn't contain expedited registers, memory or queue info. m_jstopinfo_sp = StructuredData::ParseJSON(json); } else if (key.compare("hexname") == 0) { StringExtractor name_extractor(value); std::string name; // Now convert the HEX bytes into a string value name_extractor.GetHexByteString(thread_name); } else if (key.compare("name") == 0) { thread_name = value; } else if (key.compare("qaddr") == 0) { value.getAsInteger(16, thread_dispatch_qaddr); } else if (key.compare("dispatch_queue_t") == 0) { queue_vars_valid = true; value.getAsInteger(16, dispatch_queue_t); } else if (key.compare("qname") == 0) { queue_vars_valid = true; StringExtractor name_extractor(value); // Now convert the HEX bytes into a string value name_extractor.GetHexByteString(queue_name); } else if (key.compare("qkind") == 0) { queue_kind = llvm::StringSwitch(value) .Case("serial", eQueueKindSerial) .Case("concurrent", eQueueKindConcurrent) .Default(eQueueKindUnknown); queue_vars_valid = queue_kind != eQueueKindUnknown; } else if (key.compare("qserialnum") == 0) { if (!value.getAsInteger(0, queue_serial_number)) queue_vars_valid = true; } else if (key.compare("reason") == 0) { reason = value; } else if (key.compare("description") == 0) { StringExtractor desc_extractor(value); // Now convert the HEX bytes into a string value desc_extractor.GetHexByteString(description); } else if (key.compare("memory") == 0) { // Expedited memory. GDB servers can choose to send back expedited // memory // that can populate the L1 memory cache in the process so that things // like // the frame pointer backchain can be expedited. This will help stack // backtracing be more efficient by not having to send as many memory // read // requests down the remote GDB server. // Key/value pair format: memory:=; // is a number whose base will be interpreted by the prefix: // "0x[0-9a-fA-F]+" for hex // "0[0-7]+" for octal // "[1-9]+" for decimal // is native endian ASCII hex bytes just like the register // values llvm::StringRef addr_str, bytes_str; std::tie(addr_str, bytes_str) = value.split('='); if (!addr_str.empty() && !bytes_str.empty()) { lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS; if (!addr_str.getAsInteger(0, mem_cache_addr)) { StringExtractor bytes(bytes_str); const size_t byte_size = bytes.GetBytesLeft() / 2; DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0)); const size_t bytes_copied = bytes.GetHexBytes(data_buffer_sp->GetData(), 0); if (bytes_copied == byte_size) m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp); } } } else if (key.compare("watch") == 0 || key.compare("rwatch") == 0 || key.compare("awatch") == 0) { // Support standard GDB remote stop reply packet 'TAAwatch:addr' lldb::addr_t wp_addr = LLDB_INVALID_ADDRESS; value.getAsInteger(16, wp_addr); WatchpointSP wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr); uint32_t wp_index = LLDB_INVALID_INDEX32; if (wp_sp) wp_index = wp_sp->GetHardwareIndex(); reason = "watchpoint"; StreamString ostr; ostr.Printf("%" PRIu64 " %" PRIu32, wp_addr, wp_index); description = ostr.GetString(); } else if (key.compare("library") == 0) { LoadModules(); } else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1])) { uint32_t reg = UINT32_MAX; if (!key.getAsInteger(16, reg)) expedited_register_map[reg] = std::move(value); } } if (tid == LLDB_INVALID_THREAD_ID) { // A thread id may be invalid if the response is old style 'S' packet // which does not provide the // thread information. So update the thread list and choose the first one. UpdateThreadIDList(); if (!m_thread_ids.empty()) { tid = m_thread_ids.front(); } } ThreadSP thread_sp = SetThreadStopInfo( tid, expedited_register_map, signo, thread_name, reason, description, exc_type, exc_data, thread_dispatch_qaddr, queue_vars_valid, associated_with_dispatch_queue, dispatch_queue_t, queue_name, queue_kind, queue_serial_number); return eStateStopped; } break; case 'W': case 'X': // process exited return eStateExited; default: break; } return eStateInvalid; } void ProcessGDBRemote::RefreshStateAfterStop() { std::lock_guard guard(m_thread_list_real.GetMutex()); m_thread_ids.clear(); m_thread_pcs.clear(); // Set the thread stop info. It might have a "threads" key whose value is // a list of all thread IDs in the current process, so m_thread_ids might // get set. // Scope for the lock { // Lock the thread stack while we access it std::lock_guard guard(m_last_stop_packet_mutex); // Get the number of stop packets on the stack int nItems = m_stop_packet_stack.size(); // Iterate over them for (int i = 0; i < nItems; i++) { // Get the thread stop info StringExtractorGDBRemote stop_info = m_stop_packet_stack[i]; // Process thread stop info SetThreadStopInfo(stop_info); } // Clear the thread stop stack m_stop_packet_stack.clear(); } // Check to see if SetThreadStopInfo() filled in m_thread_ids? if (m_thread_ids.empty()) { // No, we need to fetch the thread list manually UpdateThreadIDList(); } // If we have queried for a default thread id if (m_initial_tid != LLDB_INVALID_THREAD_ID) { m_thread_list.SetSelectedThreadByID(m_initial_tid); m_initial_tid = LLDB_INVALID_THREAD_ID; } // Let all threads recover from stopping and do any clean up based // on the previous thread state (if any). m_thread_list_real.RefreshStateAfterStop(); } Status ProcessGDBRemote::DoHalt(bool &caused_stop) { Status error; if (m_public_state.GetValue() == eStateAttaching) { // We are being asked to halt during an attach. We need to just close // our file handle and debugserver will go away, and we can be done... m_gdb_comm.Disconnect(); } else caused_stop = m_gdb_comm.Interrupt(); return error; } Status ProcessGDBRemote::DoDetach(bool keep_stopped) { Status error; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::DoDetach(keep_stopped: %i)", keep_stopped); error = m_gdb_comm.Detach(keep_stopped); if (log) { if (error.Success()) log->PutCString( "ProcessGDBRemote::DoDetach() detach packet sent successfully"); else log->Printf("ProcessGDBRemote::DoDetach() detach packet send failed: %s", error.AsCString() ? error.AsCString() : ""); } if (!error.Success()) return error; // Sleep for one second to let the process get all detached... StopAsyncThread(); SetPrivateState(eStateDetached); ResumePrivateStateThread(); // KillDebugserverProcess (); return error; } Status ProcessGDBRemote::DoDestroy() { Status error; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::DoDestroy()"); // There is a bug in older iOS debugservers where they don't shut down the // process // they are debugging properly. If the process is sitting at a breakpoint or // an exception, // this can cause problems with restarting. So we check to see if any of our // threads are stopped // at a breakpoint, and if so we remove all the breakpoints, resume the // process, and THEN // destroy it again. // // Note, we don't have a good way to test the version of debugserver, but I // happen to know that // the set of all the iOS debugservers which don't support // GetThreadSuffixSupported() and that of // the debugservers with this bug are equal. There really should be a better // way to test this! // // We also use m_destroy_tried_resuming to make sure we only do this once, if // we resume and then halt and // get called here to destroy again and we're still at a breakpoint or // exception, then we should // just do the straight-forward kill. // // And of course, if we weren't able to stop the process by the time we get // here, it isn't // necessary (or helpful) to do any of this. if (!m_gdb_comm.GetThreadSuffixSupported() && m_public_state.GetValue() != eStateRunning) { PlatformSP platform_sp = GetTarget().GetPlatform(); // FIXME: These should be ConstStrings so we aren't doing strcmp'ing. if (platform_sp && platform_sp->GetName() && platform_sp->GetName() == PlatformRemoteiOS::GetPluginNameStatic()) { if (m_destroy_tried_resuming) { if (log) log->PutCString("ProcessGDBRemote::DoDestroy() - Tried resuming to " "destroy once already, not doing it again."); } else { // At present, the plans are discarded and the breakpoints disabled // Process::Destroy, // but we really need it to happen here and it doesn't matter if we do // it twice. m_thread_list.DiscardThreadPlans(); DisableAllBreakpointSites(); bool stop_looks_like_crash = false; ThreadList &threads = GetThreadList(); { std::lock_guard guard(threads.GetMutex()); size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) { ThreadSP thread_sp = threads.GetThreadAtIndex(i); StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo(); StopReason reason = eStopReasonInvalid; if (stop_info_sp) reason = stop_info_sp->GetStopReason(); if (reason == eStopReasonBreakpoint || reason == eStopReasonException) { if (log) log->Printf( "ProcessGDBRemote::DoDestroy() - thread: 0x%4.4" PRIx64 " stopped with reason: %s.", thread_sp->GetProtocolID(), stop_info_sp->GetDescription()); stop_looks_like_crash = true; break; } } } if (stop_looks_like_crash) { if (log) log->PutCString("ProcessGDBRemote::DoDestroy() - Stopped at a " "breakpoint, continue and then kill."); m_destroy_tried_resuming = true; // If we are going to run again before killing, it would be good to // suspend all the threads // before resuming so they won't get into more trouble. Sadly, for // the threads stopped with // the breakpoint or exception, the exception doesn't get cleared if // it is suspended, so we do // have to run the risk of letting those threads proceed a bit. { std::lock_guard guard(threads.GetMutex()); size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) { ThreadSP thread_sp = threads.GetThreadAtIndex(i); StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo(); StopReason reason = eStopReasonInvalid; if (stop_info_sp) reason = stop_info_sp->GetStopReason(); if (reason != eStopReasonBreakpoint && reason != eStopReasonException) { if (log) log->Printf("ProcessGDBRemote::DoDestroy() - Suspending " "thread: 0x%4.4" PRIx64 " before running.", thread_sp->GetProtocolID()); thread_sp->SetResumeState(eStateSuspended); } } } Resume(); return Destroy(false); } } } } // Interrupt if our inferior is running... int exit_status = SIGABRT; std::string exit_string; if (m_gdb_comm.IsConnected()) { if (m_public_state.GetValue() != eStateAttaching) { StringExtractorGDBRemote response; bool send_async = true; GDBRemoteCommunication::ScopedTimeout(m_gdb_comm, std::chrono::seconds(3)); if (m_gdb_comm.SendPacketAndWaitForResponse("k", response, send_async) == GDBRemoteCommunication::PacketResult::Success) { char packet_cmd = response.GetChar(0); if (packet_cmd == 'W' || packet_cmd == 'X') { #if defined(__APPLE__) // For Native processes on Mac OS X, we launch through the Host // Platform, then hand the process off // to debugserver, which becomes the parent process through // "PT_ATTACH". Then when we go to kill // the process on Mac OS X we call ptrace(PT_KILL) to kill it, then we // call waitpid which returns // with no error and the correct status. But amusingly enough that // doesn't seem to actually reap // the process, but instead it is left around as a Zombie. Probably // the kernel is in the process of // switching ownership back to lldb which was the original parent, and // gets confused in the handoff. // Anyway, so call waitpid here to finally reap it. PlatformSP platform_sp(GetTarget().GetPlatform()); if (platform_sp && platform_sp->IsHost()) { int status; ::pid_t reap_pid; reap_pid = waitpid(GetID(), &status, WNOHANG); if (log) log->Printf("Reaped pid: %d, status: %d.\n", reap_pid, status); } #endif SetLastStopPacket(response); ClearThreadIDList(); exit_status = response.GetHexU8(); } else { if (log) log->Printf("ProcessGDBRemote::DoDestroy - got unexpected response " "to k packet: %s", response.GetStringRef().c_str()); exit_string.assign("got unexpected response to k packet: "); exit_string.append(response.GetStringRef()); } } else { if (log) log->Printf("ProcessGDBRemote::DoDestroy - failed to send k packet"); exit_string.assign("failed to send the k packet"); } } else { if (log) log->Printf("ProcessGDBRemote::DoDestroy - killed or interrupted while " "attaching"); exit_string.assign("killed or interrupted while attaching."); } } else { // If we missed setting the exit status on the way out, do it here. // NB set exit status can be called multiple times, the first one sets the // status. exit_string.assign("destroying when not connected to debugserver"); } SetExitStatus(exit_status, exit_string.c_str()); StopAsyncThread(); KillDebugserverProcess(); return error; } void ProcessGDBRemote::SetLastStopPacket( const StringExtractorGDBRemote &response) { const bool did_exec = response.GetStringRef().find(";reason:exec;") != std::string::npos; if (did_exec) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::SetLastStopPacket () - detected exec"); m_thread_list_real.Clear(); m_thread_list.Clear(); BuildDynamicRegisterInfo(true); m_gdb_comm.ResetDiscoverableSettings(did_exec); } // Scope the lock { // Lock the thread stack while we access it std::lock_guard guard(m_last_stop_packet_mutex); // We are are not using non-stop mode, there can only be one last stop // reply packet, so clear the list. if (GetTarget().GetNonStopModeEnabled() == false) m_stop_packet_stack.clear(); // Add this stop packet to the stop packet stack // This stack will get popped and examined when we switch to the // Stopped state m_stop_packet_stack.push_back(response); } } void ProcessGDBRemote::SetUnixSignals(const UnixSignalsSP &signals_sp) { Process::SetUnixSignals(std::make_shared(signals_sp)); } //------------------------------------------------------------------ // Process Queries //------------------------------------------------------------------ bool ProcessGDBRemote::IsAlive() { return m_gdb_comm.IsConnected() && Process::IsAlive(); } addr_t ProcessGDBRemote::GetImageInfoAddress() { // request the link map address via the $qShlibInfoAddr packet lldb::addr_t addr = m_gdb_comm.GetShlibInfoAddr(); // the loaded module list can also provides a link map address if (addr == LLDB_INVALID_ADDRESS) { LoadedModuleInfoList list; if (GetLoadedModuleList(list).Success()) addr = list.m_link_map; } return addr; } void ProcessGDBRemote::WillPublicStop() { // See if the GDB remote client supports the JSON threads info. // If so, we gather stop info for all threads, expedited registers, // expedited memory, runtime queue information (iOS and MacOSX only), // and more. Expediting memory will help stack backtracing be much // faster. Expediting registers will make sure we don't have to read // the thread registers for GPRs. m_jthreadsinfo_sp = m_gdb_comm.GetThreadsInfo(); if (m_jthreadsinfo_sp) { // Now set the stop info for each thread and also expedite any registers // and memory that was in the jThreadsInfo response. StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray(); if (thread_infos) { const size_t n = thread_infos->GetSize(); for (size_t i = 0; i < n; ++i) { StructuredData::Dictionary *thread_dict = thread_infos->GetItemAtIndex(i)->GetAsDictionary(); if (thread_dict) SetThreadStopInfo(thread_dict); } } } } //------------------------------------------------------------------ // Process Memory //------------------------------------------------------------------ size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size, Status &error) { GetMaxMemorySize(); bool binary_memory_read = m_gdb_comm.GetxPacketSupported(); // M and m packets take 2 bytes for 1 byte of memory size_t max_memory_size = binary_memory_read ? m_max_memory_size : m_max_memory_size / 2; if (size > max_memory_size) { // Keep memory read sizes down to a sane limit. This function will be // called multiple times in order to complete the task by // lldb_private::Process so it is ok to do this. size = max_memory_size; } char packet[64]; int packet_len; packet_len = ::snprintf(packet, sizeof(packet), "%c%" PRIx64 ",%" PRIx64, binary_memory_read ? 'x' : 'm', (uint64_t)addr, (uint64_t)size); assert(packet_len + 1 < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, true) == GDBRemoteCommunication::PacketResult::Success) { if (response.IsNormalResponse()) { error.Clear(); if (binary_memory_read) { // The lower level GDBRemoteCommunication packet receive layer has // already de-quoted any // 0x7d character escaping that was present in the packet size_t data_received_size = response.GetBytesLeft(); if (data_received_size > size) { // Don't write past the end of BUF if the remote debug server gave us // too // much data for some reason. data_received_size = size; } memcpy(buf, response.GetStringRef().data(), data_received_size); return data_received_size; } else { return response.GetHexBytes( llvm::MutableArrayRef((uint8_t *)buf, size), '\xdd'); } } else if (response.IsErrorResponse()) error.SetErrorStringWithFormat("memory read failed for 0x%" PRIx64, addr); else if (response.IsUnsupportedResponse()) error.SetErrorStringWithFormat( "GDB server does not support reading memory"); else error.SetErrorStringWithFormat( "unexpected response to GDB server memory read packet '%s': '%s'", packet, response.GetStringRef().c_str()); } else { error.SetErrorStringWithFormat("failed to send packet: '%s'", packet); } return 0; } size_t ProcessGDBRemote::DoWriteMemory(addr_t addr, const void *buf, size_t size, Status &error) { GetMaxMemorySize(); // M and m packets take 2 bytes for 1 byte of memory size_t max_memory_size = m_max_memory_size / 2; if (size > max_memory_size) { // Keep memory read sizes down to a sane limit. This function will be // called multiple times in order to complete the task by // lldb_private::Process so it is ok to do this. size = max_memory_size; } StreamString packet; packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size); packet.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(), endian::InlHostByteOrder()); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, true) == GDBRemoteCommunication::PacketResult::Success) { if (response.IsOKResponse()) { error.Clear(); return size; } else if (response.IsErrorResponse()) error.SetErrorStringWithFormat("memory write failed for 0x%" PRIx64, addr); else if (response.IsUnsupportedResponse()) error.SetErrorStringWithFormat( "GDB server does not support writing memory"); else error.SetErrorStringWithFormat( "unexpected response to GDB server memory write packet '%s': '%s'", packet.GetData(), response.GetStringRef().c_str()); } else { error.SetErrorStringWithFormat("failed to send packet: '%s'", packet.GetData()); } return 0; } lldb::addr_t ProcessGDBRemote::DoAllocateMemory(size_t size, uint32_t permissions, Status &error) { Log *log( GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_EXPRESSIONS)); addr_t allocated_addr = LLDB_INVALID_ADDRESS; if (m_gdb_comm.SupportsAllocDeallocMemory() != eLazyBoolNo) { allocated_addr = m_gdb_comm.AllocateMemory(size, permissions); if (allocated_addr != LLDB_INVALID_ADDRESS || m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolYes) return allocated_addr; } if (m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolNo) { // Call mmap() to create memory in the inferior.. unsigned prot = 0; if (permissions & lldb::ePermissionsReadable) prot |= eMmapProtRead; if (permissions & lldb::ePermissionsWritable) prot |= eMmapProtWrite; if (permissions & lldb::ePermissionsExecutable) prot |= eMmapProtExec; if (InferiorCallMmap(this, allocated_addr, 0, size, prot, eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) m_addr_to_mmap_size[allocated_addr] = size; else { allocated_addr = LLDB_INVALID_ADDRESS; if (log) log->Printf("ProcessGDBRemote::%s no direct stub support for memory " "allocation, and InferiorCallMmap also failed - is stub " "missing register context save/restore capability?", __FUNCTION__); } } if (allocated_addr == LLDB_INVALID_ADDRESS) error.SetErrorStringWithFormat( "unable to allocate %" PRIu64 " bytes of memory with permissions %s", (uint64_t)size, GetPermissionsAsCString(permissions)); else error.Clear(); return allocated_addr; } Status ProcessGDBRemote::GetMemoryRegionInfo(addr_t load_addr, MemoryRegionInfo ®ion_info) { Status error(m_gdb_comm.GetMemoryRegionInfo(load_addr, region_info)); return error; } Status ProcessGDBRemote::GetWatchpointSupportInfo(uint32_t &num) { Status error(m_gdb_comm.GetWatchpointSupportInfo(num)); return error; } Status ProcessGDBRemote::GetWatchpointSupportInfo(uint32_t &num, bool &after) { Status error(m_gdb_comm.GetWatchpointSupportInfo( num, after, GetTarget().GetArchitecture())); return error; } Status ProcessGDBRemote::DoDeallocateMemory(lldb::addr_t addr) { Status error; LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); switch (supported) { case eLazyBoolCalculate: // We should never be deallocating memory without allocating memory // first so we should never get eLazyBoolCalculate error.SetErrorString( "tried to deallocate memory without ever allocating memory"); break; case eLazyBoolYes: if (!m_gdb_comm.DeallocateMemory(addr)) error.SetErrorStringWithFormat( "unable to deallocate memory at 0x%" PRIx64, addr); break; case eLazyBoolNo: // Call munmap() to deallocate memory in the inferior.. { MMapMap::iterator pos = m_addr_to_mmap_size.find(addr); if (pos != m_addr_to_mmap_size.end() && InferiorCallMunmap(this, addr, pos->second)) m_addr_to_mmap_size.erase(pos); else error.SetErrorStringWithFormat( "unable to deallocate memory at 0x%" PRIx64, addr); } break; } return error; } //------------------------------------------------------------------ // Process STDIO //------------------------------------------------------------------ size_t ProcessGDBRemote::PutSTDIN(const char *src, size_t src_len, Status &error) { if (m_stdio_communication.IsConnected()) { ConnectionStatus status; m_stdio_communication.Write(src, src_len, status, NULL); } else if (m_stdin_forward) { m_gdb_comm.SendStdinNotification(src, src_len); } return 0; } Status ProcessGDBRemote::EnableBreakpointSite(BreakpointSite *bp_site) { Status error; assert(bp_site != NULL); // Get logging info Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS)); user_id_t site_id = bp_site->GetID(); // Get the breakpoint address const addr_t addr = bp_site->GetLoadAddress(); // Log that a breakpoint was requested if (log) log->Printf("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 ") address = 0x%" PRIx64, site_id, (uint64_t)addr); // Breakpoint already exists and is enabled if (bp_site->IsEnabled()) { if (log) log->Printf("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 ") address = 0x%" PRIx64 " -- SUCCESS (already enabled)", site_id, (uint64_t)addr); return error; } // Get the software breakpoint trap opcode size const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site); // SupportsGDBStoppointPacket() simply checks a boolean, indicating if this // breakpoint type // is supported by the remote stub. These are set to true by default, and // later set to false // only after we receive an unimplemented response when sending a breakpoint // packet. This means // initially that unless we were specifically instructed to use a hardware // breakpoint, LLDB will // attempt to set a software breakpoint. HardwareRequired() also queries a // boolean variable which // indicates if the user specifically asked for hardware breakpoints. If true // then we will // skip over software breakpoints. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware) && (!bp_site->HardwareRequired())) { // Try to send off a software breakpoint packet ($Z0) uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket( eBreakpointSoftware, true, addr, bp_op_size); if (error_no == 0) { // The breakpoint was placed successfully bp_site->SetEnabled(true); bp_site->SetType(BreakpointSite::eExternal); return error; } // SendGDBStoppointTypePacket() will return an error if it was unable to set // this // breakpoint. We need to differentiate between a error specific to placing // this breakpoint // or if we have learned that this breakpoint type is unsupported. To do // this, we // must test the support boolean for this breakpoint type to see if it now // indicates that // this breakpoint type is unsupported. If they are still supported then we // should return // with the error code. If they are now unsupported, then we would like to // fall through // and try another form of breakpoint. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) { if (error_no != UINT8_MAX) error.SetErrorStringWithFormat( "error: %d sending the breakpoint request", errno); else error.SetErrorString("error sending the breakpoint request"); return error; } // We reach here when software breakpoints have been found to be // unsupported. For future // calls to set a breakpoint, we will not attempt to set a breakpoint with a // type that is // known not to be supported. if (log) log->Printf("Software breakpoints are unsupported"); // So we will fall through and try a hardware breakpoint } // The process of setting a hardware breakpoint is much the same as above. We // check the // supported boolean for this breakpoint type, and if it is thought to be // supported then we // will try to set this breakpoint with a hardware breakpoint. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { // Try to send off a hardware breakpoint packet ($Z1) uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket( eBreakpointHardware, true, addr, bp_op_size); if (error_no == 0) { // The breakpoint was placed successfully bp_site->SetEnabled(true); bp_site->SetType(BreakpointSite::eHardware); return error; } // Check if the error was something other then an unsupported breakpoint // type if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { // Unable to set this hardware breakpoint if (error_no != UINT8_MAX) error.SetErrorStringWithFormat( "error: %d sending the hardware breakpoint request " "(hardware breakpoint resources might be exhausted or unavailable)", error_no); else error.SetErrorString("error sending the hardware breakpoint request " "(hardware breakpoint resources " "might be exhausted or unavailable)"); return error; } // We will reach here when the stub gives an unsupported response to a // hardware breakpoint if (log) log->Printf("Hardware breakpoints are unsupported"); // Finally we will falling through to a #trap style breakpoint } // Don't fall through when hardware breakpoints were specifically requested if (bp_site->HardwareRequired()) { error.SetErrorString("hardware breakpoints are not supported"); return error; } // As a last resort we want to place a manual breakpoint. An instruction // is placed into the process memory using memory write packets. return EnableSoftwareBreakpoint(bp_site); } Status ProcessGDBRemote::DisableBreakpointSite(BreakpointSite *bp_site) { Status error; assert(bp_site != NULL); addr_t addr = bp_site->GetLoadAddress(); user_id_t site_id = bp_site->GetID(); Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS)); if (log) log->Printf("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 ") addr = 0x%8.8" PRIx64, site_id, (uint64_t)addr); if (bp_site->IsEnabled()) { const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site); BreakpointSite::Type bp_type = bp_site->GetType(); switch (bp_type) { case BreakpointSite::eSoftware: error = DisableSoftwareBreakpoint(bp_site); break; case BreakpointSite::eHardware: if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointHardware, false, addr, bp_op_size)) error.SetErrorToGenericError(); break; case BreakpointSite::eExternal: { GDBStoppointType stoppoint_type; if (bp_site->IsHardware()) stoppoint_type = eBreakpointHardware; else stoppoint_type = eBreakpointSoftware; if (m_gdb_comm.SendGDBStoppointTypePacket(stoppoint_type, false, addr, bp_op_size)) error.SetErrorToGenericError(); } break; } if (error.Success()) bp_site->SetEnabled(false); } else { if (log) log->Printf("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", site_id, (uint64_t)addr); return error; } if (error.Success()) error.SetErrorToGenericError(); return error; } // Pre-requisite: wp != NULL. static GDBStoppointType GetGDBStoppointType(Watchpoint *wp) { assert(wp); bool watch_read = wp->WatchpointRead(); bool watch_write = wp->WatchpointWrite(); // watch_read and watch_write cannot both be false. assert(watch_read || watch_write); if (watch_read && watch_write) return eWatchpointReadWrite; else if (watch_read) return eWatchpointRead; else // Must be watch_write, then. return eWatchpointWrite; } Status ProcessGDBRemote::EnableWatchpoint(Watchpoint *wp, bool notify) { Status error; if (wp) { user_id_t watchID = wp->GetID(); addr_t addr = wp->GetLoadAddress(); Log *log( ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS)); if (log) log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ")", watchID); if (wp->IsEnabled()) { if (log) log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.", watchID, (uint64_t)addr); return error; } GDBStoppointType type = GetGDBStoppointType(wp); // Pass down an appropriate z/Z packet... if (m_gdb_comm.SupportsGDBStoppointPacket(type)) { if (m_gdb_comm.SendGDBStoppointTypePacket(type, true, addr, wp->GetByteSize()) == 0) { wp->SetEnabled(true, notify); return error; } else error.SetErrorString("sending gdb watchpoint packet failed"); } else error.SetErrorString("watchpoints not supported"); } else { error.SetErrorString("Watchpoint argument was NULL."); } if (error.Success()) error.SetErrorToGenericError(); return error; } Status ProcessGDBRemote::DisableWatchpoint(Watchpoint *wp, bool notify) { Status error; if (wp) { user_id_t watchID = wp->GetID(); Log *log( ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS)); addr_t addr = wp->GetLoadAddress(); if (log) log->Printf("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64, watchID, (uint64_t)addr); if (!wp->IsEnabled()) { if (log) log->Printf("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", watchID, (uint64_t)addr); // See also 'class WatchpointSentry' within StopInfo.cpp. // This disabling attempt might come from the user-supplied actions, we'll // route it in order for // the watchpoint object to intelligently process this action. wp->SetEnabled(false, notify); return error; } if (wp->IsHardware()) { GDBStoppointType type = GetGDBStoppointType(wp); // Pass down an appropriate z/Z packet... if (m_gdb_comm.SendGDBStoppointTypePacket(type, false, addr, wp->GetByteSize()) == 0) { wp->SetEnabled(false, notify); return error; } else error.SetErrorString("sending gdb watchpoint packet failed"); } // TODO: clear software watchpoints if we implement them } else { error.SetErrorString("Watchpoint argument was NULL."); } if (error.Success()) error.SetErrorToGenericError(); return error; } void ProcessGDBRemote::Clear() { m_flags = 0; m_thread_list_real.Clear(); m_thread_list.Clear(); } Status ProcessGDBRemote::DoSignal(int signo) { Status error; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::DoSignal (signal = %d)", signo); if (!m_gdb_comm.SendAsyncSignal(signo)) error.SetErrorStringWithFormat("failed to send signal %i", signo); return error; } Status ProcessGDBRemote::EstablishConnectionIfNeeded(const ProcessInfo &process_info) { // Make sure we aren't already connected? if (m_gdb_comm.IsConnected()) return Status(); PlatformSP platform_sp(GetTarget().GetPlatform()); if (platform_sp && !platform_sp->IsHost()) return Status("Lost debug server connection"); auto error = LaunchAndConnectToDebugserver(process_info); if (error.Fail()) { const char *error_string = error.AsCString(); if (error_string == nullptr) error_string = "unable to launch " DEBUGSERVER_BASENAME; } return error; } #if defined(__APPLE__) #define USE_SOCKETPAIR_FOR_LOCAL_CONNECTION 1 #endif #ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION static bool SetCloexecFlag(int fd) { #if defined(FD_CLOEXEC) int flags = ::fcntl(fd, F_GETFD); if (flags == -1) return false; return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0); #else return false; #endif } #endif Status ProcessGDBRemote::LaunchAndConnectToDebugserver( const ProcessInfo &process_info) { using namespace std::placeholders; // For _1, _2, etc. Status error; if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID) { // If we locate debugserver, keep that located version around static FileSpec g_debugserver_file_spec; ProcessLaunchInfo debugserver_launch_info; // Make debugserver run in its own session so signals generated by // special terminal key sequences (^C) don't affect debugserver. debugserver_launch_info.SetLaunchInSeparateProcessGroup(true); const std::weak_ptr this_wp = std::static_pointer_cast(shared_from_this()); debugserver_launch_info.SetMonitorProcessCallback( std::bind(MonitorDebugserverProcess, this_wp, _1, _2, _3, _4), false); debugserver_launch_info.SetUserID(process_info.GetUserID()); int communication_fd = -1; #ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION // Auto close the sockets we might open up unless everything goes OK. This // helps us not leak file descriptors when things go wrong. lldb_utility::CleanUp our_socket(-1, -1, close); lldb_utility::CleanUp gdb_socket(-1, -1, close); // Use a socketpair on Apple for now until other platforms can verify it // works and is fast enough { int sockets[2]; /* the pair of socket descriptors */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) { error.SetErrorToErrno(); return error; } our_socket.set(sockets[0]); gdb_socket.set(sockets[1]); } // Don't let any child processes inherit our communication socket SetCloexecFlag(our_socket.get()); communication_fd = gdb_socket.get(); #endif error = m_gdb_comm.StartDebugserverProcess( nullptr, GetTarget().GetPlatform().get(), debugserver_launch_info, nullptr, nullptr, communication_fd); if (error.Success()) m_debugserver_pid = debugserver_launch_info.GetProcessID(); else m_debugserver_pid = LLDB_INVALID_PROCESS_ID; if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) { #ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION // Our process spawned correctly, we can now set our connection to use our // end of the socket pair m_gdb_comm.SetConnection( new ConnectionFileDescriptor(our_socket.release(), true)); #endif StartAsyncThread(); } if (error.Fail()) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("failed to start debugserver process: %s", error.AsCString()); return error; } if (m_gdb_comm.IsConnected()) { // Finish the connection process by doing the handshake without connecting // (send NULL URL) ConnectToDebugserver(""); } else { error.SetErrorString("connection failed"); } } return error; } bool ProcessGDBRemote::MonitorDebugserverProcess( std::weak_ptr process_wp, lldb::pid_t debugserver_pid, bool exited, // True if the process did exit int signo, // Zero for no signal int exit_status // Exit value of process if signal is zero ) { // "debugserver_pid" argument passed in is the process ID for // debugserver that we are tracking... Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); const bool handled = true; if (log) log->Printf("ProcessGDBRemote::%s(process_wp, pid=%" PRIu64 ", signo=%i (0x%x), exit_status=%i)", __FUNCTION__, debugserver_pid, signo, signo, exit_status); std::shared_ptr process_sp = process_wp.lock(); if (log) log->Printf("ProcessGDBRemote::%s(process = %p)", __FUNCTION__, static_cast(process_sp.get())); if (!process_sp || process_sp->m_debugserver_pid != debugserver_pid) return handled; // Sleep for a half a second to make sure our inferior process has // time to set its exit status before we set it incorrectly when // both the debugserver and the inferior process shut down. usleep(500000); // If our process hasn't yet exited, debugserver might have died. // If the process did exit, then we are reaping it. const StateType state = process_sp->GetState(); if (state != eStateInvalid && state != eStateUnloaded && state != eStateExited && state != eStateDetached) { char error_str[1024]; if (signo) { const char *signal_cstr = process_sp->GetUnixSignals()->GetSignalAsCString(signo); if (signal_cstr) ::snprintf(error_str, sizeof(error_str), DEBUGSERVER_BASENAME " died with signal %s", signal_cstr); else ::snprintf(error_str, sizeof(error_str), DEBUGSERVER_BASENAME " died with signal %i", signo); } else { ::snprintf(error_str, sizeof(error_str), DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x", exit_status); } process_sp->SetExitStatus(-1, error_str); } // Debugserver has exited we need to let our ProcessGDBRemote // know that it no longer has a debugserver instance process_sp->m_debugserver_pid = LLDB_INVALID_PROCESS_ID; return handled; } void ProcessGDBRemote::KillDebugserverProcess() { m_gdb_comm.Disconnect(); if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) { Host::Kill(m_debugserver_pid, SIGINT); m_debugserver_pid = LLDB_INVALID_PROCESS_ID; } } void ProcessGDBRemote::Initialize() { static llvm::once_flag g_once_flag; llvm::call_once(g_once_flag, []() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, DebuggerInitialize); }); } void ProcessGDBRemote::DebuggerInitialize(Debugger &debugger) { if (!PluginManager::GetSettingForProcessPlugin( debugger, PluginProperties::GetSettingName())) { const bool is_global_setting = true; PluginManager::CreateSettingForProcessPlugin( debugger, GetGlobalPluginProperties()->GetValueProperties(), ConstString("Properties for the gdb-remote process plug-in."), is_global_setting); } } bool ProcessGDBRemote::StartAsyncThread() { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::%s ()", __FUNCTION__); std::lock_guard guard(m_async_thread_state_mutex); if (!m_async_thread.IsJoinable()) { // Create a thread that watches our internal state and controls which // events make it to clients (into the DCProcess event queue). m_async_thread = ThreadLauncher::LaunchThread("", ProcessGDBRemote::AsyncThread, this, NULL); } else if (log) log->Printf("ProcessGDBRemote::%s () - Called when Async thread was " "already running.", __FUNCTION__); return m_async_thread.IsJoinable(); } void ProcessGDBRemote::StopAsyncThread() { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::%s ()", __FUNCTION__); std::lock_guard guard(m_async_thread_state_mutex); if (m_async_thread.IsJoinable()) { m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncThreadShouldExit); // This will shut down the async thread. m_gdb_comm.Disconnect(); // Disconnect from the debug server. // Stop the stdio thread m_async_thread.Join(nullptr); m_async_thread.Reset(); } else if (log) log->Printf( "ProcessGDBRemote::%s () - Called when Async thread was not running.", __FUNCTION__); } bool ProcessGDBRemote::HandleNotifyPacket(StringExtractorGDBRemote &packet) { // get the packet at a string const std::string &pkt = packet.GetStringRef(); // skip %stop: StringExtractorGDBRemote stop_info(pkt.c_str() + 5); // pass as a thread stop info packet SetLastStopPacket(stop_info); // check for more stop reasons HandleStopReplySequence(); // if the process is stopped then we need to fake a resume // so that we can stop properly with the new break. This // is possible due to SetPrivateState() broadcasting the // state change as a side effect. if (GetPrivateState() == lldb::StateType::eStateStopped) { SetPrivateState(lldb::StateType::eStateRunning); } // since we have some stopped packets we can halt the process SetPrivateState(lldb::StateType::eStateStopped); return true; } thread_result_t ProcessGDBRemote::AsyncThread(void *arg) { ProcessGDBRemote *process = (ProcessGDBRemote *)arg; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread starting...", __FUNCTION__, arg, process->GetID()); EventSP event_sp; bool done = false; while (!done) { if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp)...", __FUNCTION__, arg, process->GetID()); if (process->m_async_listener_sp->GetEvent(event_sp, llvm::None)) { const uint32_t event_type = event_sp->GetType(); if (event_sp->BroadcasterIs(&process->m_async_broadcaster)) { if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") Got an event of type: %d...", __FUNCTION__, arg, process->GetID(), event_type); switch (event_type) { case eBroadcastBitAsyncContinue: { const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event_sp.get()); if (continue_packet) { const char *continue_cstr = (const char *)continue_packet->GetBytes(); const size_t continue_cstr_len = continue_packet->GetByteSize(); if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got eBroadcastBitAsyncContinue: %s", __FUNCTION__, arg, process->GetID(), continue_cstr); if (::strstr(continue_cstr, "vAttach") == NULL) process->SetPrivateState(eStateRunning); StringExtractorGDBRemote response; // If in Non-Stop-Mode if (process->GetTarget().GetNonStopModeEnabled()) { // send the vCont packet if (!process->GetGDBRemote().SendvContPacket( llvm::StringRef(continue_cstr, continue_cstr_len), response)) { // Something went wrong done = true; break; } } // If in All-Stop-Mode else { StateType stop_state = process->GetGDBRemote().SendContinuePacketAndWaitForResponse( *process, *process->GetUnixSignals(), llvm::StringRef(continue_cstr, continue_cstr_len), response); // We need to immediately clear the thread ID list so we are sure // to get a valid list of threads. // The thread ID list might be contained within the "response", or // the stop reply packet that // caused the stop. So clear it now before we give the stop reply // packet to the process // using the process->SetLastStopPacket()... process->ClearThreadIDList(); switch (stop_state) { case eStateStopped: case eStateCrashed: case eStateSuspended: process->SetLastStopPacket(response); process->SetPrivateState(stop_state); break; case eStateExited: { process->SetLastStopPacket(response); process->ClearThreadIDList(); response.SetFilePos(1); int exit_status = response.GetHexU8(); std::string desc_string; if (response.GetBytesLeft() > 0 && response.GetChar('-') == ';') { llvm::StringRef desc_str; llvm::StringRef desc_token; while (response.GetNameColonValue(desc_token, desc_str)) { if (desc_token != "description") continue; StringExtractor extractor(desc_str); extractor.GetHexByteString(desc_string); } } process->SetExitStatus(exit_status, desc_string.c_str()); done = true; break; } case eStateInvalid: { // Check to see if we were trying to attach and if we got back // the "E87" error code from debugserver -- this indicates that // the process is not debuggable. Return a slightly more // helpful // error message about why the attach failed. if (::strstr(continue_cstr, "vAttach") != NULL && response.GetError() == 0x87) { process->SetExitStatus(-1, "cannot attach to process due to " "System Integrity Protection"); } // E01 code from vAttach means that the attach failed if (::strstr(continue_cstr, "vAttach") != NULL && response.GetError() == 0x1) { process->SetExitStatus(-1, "unable to attach"); } else { process->SetExitStatus(-1, "lost connection"); } break; } default: process->SetPrivateState(stop_state); break; } // switch(stop_state) } // else // if in All-stop-mode } // if (continue_packet) } // case eBroadcastBitAysncContinue break; case eBroadcastBitAsyncThreadShouldExit: if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got eBroadcastBitAsyncThreadShouldExit...", __FUNCTION__, arg, process->GetID()); done = true; break; default: if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type); done = true; break; } } else if (event_sp->BroadcasterIs(&process->m_gdb_comm)) { switch (event_type) { case Communication::eBroadcastBitReadThreadDidExit: process->SetExitStatus(-1, "lost connection"); done = true; break; case GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify: { lldb_private::Event *event = event_sp.get(); const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event); StringExtractorGDBRemote notify( (const char *)continue_packet->GetBytes()); // Hand this over to the process to handle process->HandleNotifyPacket(notify); break; } default: if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type); done = true; break; } } } else { if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp) => false", __FUNCTION__, arg, process->GetID()); done = true; } } if (log) log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread exiting...", __FUNCTION__, arg, process->GetID()); return NULL; } // uint32_t // ProcessGDBRemote::ListProcessesMatchingName (const char *name, StringList // &matches, std::vector &pids) //{ // // If we are planning to launch the debugserver remotely, then we need to // fire up a debugserver // // process and ask it for the list of processes. But if we are local, we // can let the Host do it. // if (m_local_debugserver) // { // return Host::ListProcessesMatchingName (name, matches, pids); // } // else // { // // FIXME: Implement talking to the remote debugserver. // return 0; // } // //} // bool ProcessGDBRemote::NewThreadNotifyBreakpointHit( void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) { // I don't think I have to do anything here, just make sure I notice the new // thread when it starts to // run so I can stop it if that's what I want to do. Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log) log->Printf("Hit New Thread Notification breakpoint."); return false; } Status ProcessGDBRemote::UpdateAutomaticSignalFiltering() { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); LLDB_LOG(log, "Check if need to update ignored signals"); // QPassSignals package is not supported by the server, // there is no way we can ignore any signals on server side. if (!m_gdb_comm.GetQPassSignalsSupported()) return Status(); // No signals, nothing to send. if (m_unix_signals_sp == nullptr) return Status(); // Signals' version hasn't changed, no need to send anything. uint64_t new_signals_version = m_unix_signals_sp->GetVersion(); if (new_signals_version == m_last_signals_version) { LLDB_LOG(log, "Signals' version hasn't changed. version={0}", m_last_signals_version); return Status(); } auto signals_to_ignore = m_unix_signals_sp->GetFilteredSignals(false, false, false); Status error = m_gdb_comm.SendSignalsToIgnore(signals_to_ignore); LLDB_LOG(log, "Signals' version changed. old version={0}, new version={1}, " "signals ignored={2}, update result={3}", m_last_signals_version, new_signals_version, signals_to_ignore.size(), error); if (error.Success()) m_last_signals_version = new_signals_version; return error; } bool ProcessGDBRemote::StartNoticingNewThreads() { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (m_thread_create_bp_sp) { if (log && log->GetVerbose()) log->Printf("Enabled noticing new thread breakpoint."); m_thread_create_bp_sp->SetEnabled(true); } else { PlatformSP platform_sp(GetTarget().GetPlatform()); if (platform_sp) { m_thread_create_bp_sp = platform_sp->SetThreadCreationBreakpoint(GetTarget()); if (m_thread_create_bp_sp) { if (log && log->GetVerbose()) log->Printf( "Successfully created new thread notification breakpoint %i", m_thread_create_bp_sp->GetID()); m_thread_create_bp_sp->SetCallback( ProcessGDBRemote::NewThreadNotifyBreakpointHit, this, true); } else { if (log) log->Printf("Failed to create new thread notification breakpoint."); } } } return m_thread_create_bp_sp.get() != NULL; } bool ProcessGDBRemote::StopNoticingNewThreads() { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log && log->GetVerbose()) log->Printf("Disabling new thread notification breakpoint."); if (m_thread_create_bp_sp) m_thread_create_bp_sp->SetEnabled(false); return true; } DynamicLoader *ProcessGDBRemote::GetDynamicLoader() { if (m_dyld_ap.get() == NULL) m_dyld_ap.reset(DynamicLoader::FindPlugin(this, NULL)); return m_dyld_ap.get(); } Status ProcessGDBRemote::SendEventData(const char *data) { int return_value; bool was_supported; Status error; return_value = m_gdb_comm.SendLaunchEventDataPacket(data, &was_supported); if (return_value != 0) { if (!was_supported) error.SetErrorString("Sending events is not supported for this process."); else error.SetErrorStringWithFormat("Error sending event data: %d.", return_value); } return error; } const DataBufferSP ProcessGDBRemote::GetAuxvData() { DataBufferSP buf; if (m_gdb_comm.GetQXferAuxvReadSupported()) { std::string response_string; if (m_gdb_comm.SendPacketsAndConcatenateResponses("qXfer:auxv:read::", response_string) == GDBRemoteCommunication::PacketResult::Success) buf.reset(new DataBufferHeap(response_string.c_str(), response_string.length())); } return buf; } StructuredData::ObjectSP ProcessGDBRemote::GetExtendedInfoForThread(lldb::tid_t tid) { StructuredData::ObjectSP object_sp; if (m_gdb_comm.GetThreadExtendedInfoSupported()) { StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); SystemRuntime *runtime = GetSystemRuntime(); if (runtime) { runtime->AddThreadExtendedInfoPacketHints(args_dict); } args_dict->GetAsDictionary()->AddIntegerItem("thread", tid); StreamString packet; packet << "jThreadExtendedInfo:"; args_dict->Dump(packet, false); // FIXME the final character of a JSON dictionary, '}', is the escape // character in gdb-remote binary mode. lldb currently doesn't escape // these characters in its packet output -- so we add the quoted version // of the } character here manually in case we talk to a debugserver which // un-escapes the characters at packet read time. packet << (char)(0x7d ^ 0x20); StringExtractorGDBRemote response; response.SetResponseValidatorToJSON(); if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, false) == GDBRemoteCommunication::PacketResult::Success) { StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { if (!response.Empty()) { object_sp = StructuredData::ParseJSON(response.GetStringRef()); } } } } return object_sp; } StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos( lldb::addr_t image_list_address, lldb::addr_t image_count) { StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); args_dict->GetAsDictionary()->AddIntegerItem("image_list_address", image_list_address); args_dict->GetAsDictionary()->AddIntegerItem("image_count", image_count); return GetLoadedDynamicLibrariesInfos_sender(args_dict); } StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos() { StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); args_dict->GetAsDictionary()->AddBooleanItem("fetch_all_solibs", true); return GetLoadedDynamicLibrariesInfos_sender(args_dict); } StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos( const std::vector &load_addresses) { StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); StructuredData::ArraySP addresses(new StructuredData::Array); for (auto addr : load_addresses) { StructuredData::ObjectSP addr_sp(new StructuredData::Integer(addr)); addresses->AddItem(addr_sp); } args_dict->GetAsDictionary()->AddItem("solib_addresses", addresses); return GetLoadedDynamicLibrariesInfos_sender(args_dict); } StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos_sender( StructuredData::ObjectSP args_dict) { StructuredData::ObjectSP object_sp; if (m_gdb_comm.GetLoadedDynamicLibrariesInfosSupported()) { // Scope for the scoped timeout object GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm, std::chrono::seconds(10)); StreamString packet; packet << "jGetLoadedDynamicLibrariesInfos:"; args_dict->Dump(packet, false); // FIXME the final character of a JSON dictionary, '}', is the escape // character in gdb-remote binary mode. lldb currently doesn't escape // these characters in its packet output -- so we add the quoted version // of the } character here manually in case we talk to a debugserver which // un-escapes the characters at packet read time. packet << (char)(0x7d ^ 0x20); StringExtractorGDBRemote response; response.SetResponseValidatorToJSON(); if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, false) == GDBRemoteCommunication::PacketResult::Success) { StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { if (!response.Empty()) { object_sp = StructuredData::ParseJSON(response.GetStringRef()); } } } } return object_sp; } StructuredData::ObjectSP ProcessGDBRemote::GetSharedCacheInfo() { StructuredData::ObjectSP object_sp; StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); if (m_gdb_comm.GetSharedCacheInfoSupported()) { StreamString packet; packet << "jGetSharedCacheInfo:"; args_dict->Dump(packet, false); // FIXME the final character of a JSON dictionary, '}', is the escape // character in gdb-remote binary mode. lldb currently doesn't escape // these characters in its packet output -- so we add the quoted version // of the } character here manually in case we talk to a debugserver which // un-escapes the characters at packet read time. packet << (char)(0x7d ^ 0x20); StringExtractorGDBRemote response; response.SetResponseValidatorToJSON(); if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, false) == GDBRemoteCommunication::PacketResult::Success) { StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { if (!response.Empty()) { object_sp = StructuredData::ParseJSON(response.GetStringRef()); } } } } return object_sp; } Status ProcessGDBRemote::ConfigureStructuredData( const ConstString &type_name, const StructuredData::ObjectSP &config_sp) { return m_gdb_comm.ConfigureRemoteStructuredData(type_name, config_sp); } // Establish the largest memory read/write payloads we should use. // If the remote stub has a max packet size, stay under that size. // // If the remote stub's max packet size is crazy large, use a // reasonable largeish default. // // If the remote stub doesn't advertise a max packet size, use a // conservative default. void ProcessGDBRemote::GetMaxMemorySize() { const uint64_t reasonable_largeish_default = 128 * 1024; const uint64_t conservative_default = 512; if (m_max_memory_size == 0) { uint64_t stub_max_size = m_gdb_comm.GetRemoteMaxPacketSize(); if (stub_max_size != UINT64_MAX && stub_max_size != 0) { // Save the stub's claimed maximum packet size m_remote_stub_max_memory_size = stub_max_size; // Even if the stub says it can support ginormous packets, // don't exceed our reasonable largeish default packet size. if (stub_max_size > reasonable_largeish_default) { stub_max_size = reasonable_largeish_default; } // Memory packet have other overheads too like Maddr,size:#NN // Instead of calculating the bytes taken by size and addr every // time, we take a maximum guess here. if (stub_max_size > 70) stub_max_size -= 32 + 32 + 6; else { // In unlikely scenario that max packet size is less then 70, we will // hope that data being written is small enough to fit. Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet( GDBR_LOG_COMM | GDBR_LOG_MEMORY)); if (log) log->Warning("Packet size is too small. " "LLDB may face problems while writing memory"); } m_max_memory_size = stub_max_size; } else { m_max_memory_size = conservative_default; } } } void ProcessGDBRemote::SetUserSpecifiedMaxMemoryTransferSize( uint64_t user_specified_max) { if (user_specified_max != 0) { GetMaxMemorySize(); if (m_remote_stub_max_memory_size != 0) { if (m_remote_stub_max_memory_size < user_specified_max) { m_max_memory_size = m_remote_stub_max_memory_size; // user specified a // packet size too // big, go as big // as the remote stub says we can go. } else { m_max_memory_size = user_specified_max; // user's packet size is good } } else { m_max_memory_size = user_specified_max; // user's packet size is probably fine } } } bool ProcessGDBRemote::GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, ModuleSpec &module_spec) { Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); const ModuleCacheKey key(module_file_spec.GetPath(), arch.GetTriple().getTriple()); auto cached = m_cached_module_specs.find(key); if (cached != m_cached_module_specs.end()) { module_spec = cached->second; return bool(module_spec); } if (!m_gdb_comm.GetModuleInfo(module_file_spec, arch, module_spec)) { if (log) log->Printf("ProcessGDBRemote::%s - failed to get module info for %s:%s", __FUNCTION__, module_file_spec.GetPath().c_str(), arch.GetTriple().getTriple().c_str()); return false; } if (log) { StreamString stream; module_spec.Dump(stream); log->Printf("ProcessGDBRemote::%s - got module info for (%s:%s) : %s", __FUNCTION__, module_file_spec.GetPath().c_str(), arch.GetTriple().getTriple().c_str(), stream.GetData()); } m_cached_module_specs[key] = module_spec; return true; } void ProcessGDBRemote::PrefetchModuleSpecs( llvm::ArrayRef module_file_specs, const llvm::Triple &triple) { auto module_specs = m_gdb_comm.GetModulesInfo(module_file_specs, triple); if (module_specs) { for (const FileSpec &spec : module_file_specs) m_cached_module_specs[ModuleCacheKey(spec.GetPath(), triple.getTriple())] = ModuleSpec(); for (const ModuleSpec &spec : *module_specs) m_cached_module_specs[ModuleCacheKey(spec.GetFileSpec().GetPath(), triple.getTriple())] = spec; } } bool ProcessGDBRemote::GetHostOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) { if (m_gdb_comm.GetOSVersion(major, minor, update)) return true; // We failed to get the host OS version, defer to the base // implementation to correctly invalidate the arguments. return Process::GetHostOSVersion(major, minor, update); } namespace { typedef std::vector stringVec; typedef std::vector GDBServerRegisterVec; struct RegisterSetInfo { ConstString name; }; typedef std::map RegisterSetMap; struct GdbServerTargetInfo { std::string arch; std::string osabi; stringVec includes; RegisterSetMap reg_set_map; XMLNode feature_node; }; bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemoteDynamicRegisterInfo &dyn_reg_info, ABISP abi_sp, uint32_t &cur_reg_num, uint32_t ®_offset) { if (!feature_node) return false; feature_node.ForEachChildElementWithName( "reg", [&target_info, &dyn_reg_info, &cur_reg_num, ®_offset, &abi_sp](const XMLNode ®_node) -> bool { std::string gdb_group; std::string gdb_type; ConstString reg_name; ConstString alt_name; ConstString set_name; std::vector value_regs; std::vector invalidate_regs; std::vector dwarf_opcode_bytes; bool encoding_set = false; bool format_set = false; RegisterInfo reg_info = { NULL, // Name NULL, // Alt name 0, // byte size reg_offset, // offset eEncodingUint, // encoding eFormatHex, // format { LLDB_INVALID_REGNUM, // eh_frame reg num LLDB_INVALID_REGNUM, // DWARF reg num LLDB_INVALID_REGNUM, // generic reg num cur_reg_num, // process plugin reg num cur_reg_num // native register number }, NULL, NULL, NULL, // Dwarf Expression opcode bytes pointer 0 // Dwarf Expression opcode bytes length }; reg_node.ForEachAttribute([&target_info, &gdb_group, &gdb_type, ®_name, &alt_name, &set_name, &value_regs, &invalidate_regs, &encoding_set, &format_set, ®_info, ®_offset, &dwarf_opcode_bytes]( const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "name") { reg_name.SetString(value); } else if (name == "bitsize") { reg_info.byte_size = StringConvert::ToUInt32(value.data(), 0, 0) / CHAR_BIT; } else if (name == "type") { gdb_type = value.str(); } else if (name == "group") { gdb_group = value.str(); } else if (name == "regnum") { const uint32_t regnum = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); if (regnum != LLDB_INVALID_REGNUM) { reg_info.kinds[eRegisterKindProcessPlugin] = regnum; } } else if (name == "offset") { reg_offset = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); } else if (name == "altname") { alt_name.SetString(value); } else if (name == "encoding") { encoding_set = true; reg_info.encoding = Args::StringToEncoding(value, eEncodingUint); } else if (name == "format") { format_set = true; Format format = eFormatInvalid; if (Args::StringToFormat(value.data(), format, NULL).Success()) reg_info.format = format; else if (value == "vector-sint8") reg_info.format = eFormatVectorOfSInt8; else if (value == "vector-uint8") reg_info.format = eFormatVectorOfUInt8; else if (value == "vector-sint16") reg_info.format = eFormatVectorOfSInt16; else if (value == "vector-uint16") reg_info.format = eFormatVectorOfUInt16; else if (value == "vector-sint32") reg_info.format = eFormatVectorOfSInt32; else if (value == "vector-uint32") reg_info.format = eFormatVectorOfUInt32; else if (value == "vector-float32") reg_info.format = eFormatVectorOfFloat32; else if (value == "vector-uint64") reg_info.format = eFormatVectorOfUInt64; else if (value == "vector-uint128") reg_info.format = eFormatVectorOfUInt128; } else if (name == "group_id") { const uint32_t set_id = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); RegisterSetMap::const_iterator pos = target_info.reg_set_map.find(set_id); if (pos != target_info.reg_set_map.end()) set_name = pos->second.name; } else if (name == "gcc_regnum" || name == "ehframe_regnum") { reg_info.kinds[eRegisterKindEHFrame] = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); } else if (name == "dwarf_regnum") { reg_info.kinds[eRegisterKindDWARF] = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); } else if (name == "generic") { reg_info.kinds[eRegisterKindGeneric] = Args::StringToGenericRegister(value); } else if (name == "value_regnums") { SplitCommaSeparatedRegisterNumberString(value, value_regs, 0); } else if (name == "invalidate_regnums") { SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 0); } else if (name == "dynamic_size_dwarf_expr_bytes") { StringExtractor opcode_extractor; std::string opcode_string = value.str(); size_t dwarf_opcode_len = opcode_string.length() / 2; assert(dwarf_opcode_len > 0); dwarf_opcode_bytes.resize(dwarf_opcode_len); reg_info.dynamic_size_dwarf_len = dwarf_opcode_len; opcode_extractor.GetStringRef().swap(opcode_string); uint32_t ret_val = opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes); assert(dwarf_opcode_len == ret_val); UNUSED_IF_ASSERT_DISABLED(ret_val); reg_info.dynamic_size_dwarf_expr_bytes = dwarf_opcode_bytes.data(); } else { printf("unhandled attribute %s = %s\n", name.data(), value.data()); } return true; // Keep iterating through all attributes }); if (!gdb_type.empty() && !(encoding_set || format_set)) { if (gdb_type.find("int") == 0) { reg_info.format = eFormatHex; reg_info.encoding = eEncodingUint; } else if (gdb_type == "data_ptr" || gdb_type == "code_ptr") { reg_info.format = eFormatAddressInfo; reg_info.encoding = eEncodingUint; } else if (gdb_type == "i387_ext" || gdb_type == "float") { reg_info.format = eFormatFloat; reg_info.encoding = eEncodingIEEE754; } } // Only update the register set name if we didn't get a "reg_set" // attribute. // "set_name" will be empty if we didn't have a "reg_set" attribute. if (!set_name && !gdb_group.empty()) set_name.SetCString(gdb_group.c_str()); reg_info.byte_offset = reg_offset; assert(reg_info.byte_size != 0); reg_offset += reg_info.byte_size; if (!value_regs.empty()) { value_regs.push_back(LLDB_INVALID_REGNUM); reg_info.value_regs = value_regs.data(); } if (!invalidate_regs.empty()) { invalidate_regs.push_back(LLDB_INVALID_REGNUM); reg_info.invalidate_regs = invalidate_regs.data(); } ++cur_reg_num; AugmentRegisterInfoViaABI(reg_info, reg_name, abi_sp); dyn_reg_info.AddRegister(reg_info, reg_name, alt_name, set_name); return true; // Keep iterating through all "reg" elements }); return true; } } // namespace {} // query the target of gdb-remote for extended target information // return: 'true' on success // 'false' on failure bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { // Make sure LLDB has an XML parser it can use first if (!XMLDocument::XMLEnabled()) return false; // redirect libxml2's error handler since the default prints to stdout GDBRemoteCommunicationClient &comm = m_gdb_comm; // check that we have extended feature read support if (!comm.GetQXferFeaturesReadSupported()) return false; // request the target xml file std::string raw; lldb_private::Status lldberr; if (!comm.ReadExtFeature(ConstString("features"), ConstString("target.xml"), raw, lldberr)) { return false; } XMLDocument xml_document; if (xml_document.ParseMemory(raw.c_str(), raw.size(), "target.xml")) { GdbServerTargetInfo target_info; XMLNode target_node = xml_document.GetRootElement("target"); if (target_node) { XMLNode feature_node; target_node.ForEachChildElement([&target_info, &feature_node]( const XMLNode &node) -> bool { llvm::StringRef name = node.GetName(); if (name == "architecture") { node.GetElementText(target_info.arch); } else if (name == "osabi") { node.GetElementText(target_info.osabi); } else if (name == "xi:include" || name == "include") { llvm::StringRef href = node.GetAttributeValue("href"); if (!href.empty()) target_info.includes.push_back(href.str()); } else if (name == "feature") { feature_node = node; } else if (name == "groups") { node.ForEachChildElementWithName( "group", [&target_info](const XMLNode &node) -> bool { uint32_t set_id = UINT32_MAX; RegisterSetInfo set_info; node.ForEachAttribute( [&set_id, &set_info](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "id") set_id = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); if (name == "name") set_info.name = ConstString(value); return true; // Keep iterating through all attributes }); if (set_id != UINT32_MAX) target_info.reg_set_map[set_id] = set_info; return true; // Keep iterating through all "group" elements }); } return true; // Keep iterating through all children of the target_node }); // Initialize these outside of ParseRegisters, since they should not be // reset inside each include feature uint32_t cur_reg_num = 0; uint32_t reg_offset = 0; // Don't use Process::GetABI, this code gets called from DidAttach, and in // that context we haven't // set the Target's architecture yet, so the ABI is also potentially // incorrect. ABISP abi_to_use_sp = ABI::FindPlugin(arch_to_use); if (feature_node) { ParseRegisters(feature_node, target_info, this->m_register_info, abi_to_use_sp, cur_reg_num, reg_offset); } for (const auto &include : target_info.includes) { // request register file std::string xml_data; if (!comm.ReadExtFeature(ConstString("features"), ConstString(include), xml_data, lldberr)) continue; XMLDocument include_xml_document; include_xml_document.ParseMemory(xml_data.data(), xml_data.size(), include.c_str()); XMLNode include_feature_node = include_xml_document.GetRootElement("feature"); if (include_feature_node) { ParseRegisters(include_feature_node, target_info, this->m_register_info, abi_to_use_sp, cur_reg_num, reg_offset); } } this->m_register_info.Finalize(arch_to_use); } } return m_register_info.GetNumRegisters() > 0; } Status ProcessGDBRemote::GetLoadedModuleList(LoadedModuleInfoList &list) { // Make sure LLDB has an XML parser it can use first if (!XMLDocument::XMLEnabled()) return Status(0, ErrorType::eErrorTypeGeneric); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS); if (log) log->Printf("ProcessGDBRemote::%s", __FUNCTION__); GDBRemoteCommunicationClient &comm = m_gdb_comm; // check that we have extended feature read support if (comm.GetQXferLibrariesSVR4ReadSupported()) { list.clear(); // request the loaded library list std::string raw; lldb_private::Status lldberr; if (!comm.ReadExtFeature(ConstString("libraries-svr4"), ConstString(""), raw, lldberr)) return Status(0, ErrorType::eErrorTypeGeneric); // parse the xml file in memory if (log) log->Printf("parsing: %s", raw.c_str()); XMLDocument doc; if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) return Status(0, ErrorType::eErrorTypeGeneric); XMLNode root_element = doc.GetRootElement("library-list-svr4"); if (!root_element) return Status(); // main link map structure llvm::StringRef main_lm = root_element.GetAttributeValue("main-lm"); if (!main_lm.empty()) { list.m_link_map = StringConvert::ToUInt64(main_lm.data(), LLDB_INVALID_ADDRESS, 0); } root_element.ForEachChildElementWithName( "library", [log, &list](const XMLNode &library) -> bool { LoadedModuleInfoList::LoadedModuleInfo module; library.ForEachAttribute( [&module](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "name") module.set_name(value.str()); else if (name == "lm") { // the address of the link_map struct. module.set_link_map(StringConvert::ToUInt64( value.data(), LLDB_INVALID_ADDRESS, 0)); } else if (name == "l_addr") { // the displacement as read from the field 'l_addr' of the // link_map struct. module.set_base(StringConvert::ToUInt64( value.data(), LLDB_INVALID_ADDRESS, 0)); // base address is always a displacement, not an absolute // value. module.set_base_is_offset(true); } else if (name == "l_ld") { // the memory address of the libraries PT_DYAMIC section. module.set_dynamic(StringConvert::ToUInt64( value.data(), LLDB_INVALID_ADDRESS, 0)); } return true; // Keep iterating over all properties of "library" }); if (log) { std::string name; lldb::addr_t lm = 0, base = 0, ld = 0; bool base_is_offset; module.get_name(name); module.get_link_map(lm); module.get_base(base); module.get_base_is_offset(base_is_offset); module.get_dynamic(ld); log->Printf("found (link_map:0x%08" PRIx64 ", base:0x%08" PRIx64 "[%s], ld:0x%08" PRIx64 ", name:'%s')", lm, base, (base_is_offset ? "offset" : "absolute"), ld, name.c_str()); } list.add(module); return true; // Keep iterating over all "library" elements in the root // node }); if (log) log->Printf("found %" PRId32 " modules in total", (int)list.m_list.size()); } else if (comm.GetQXferLibrariesReadSupported()) { list.clear(); // request the loaded library list std::string raw; lldb_private::Status lldberr; if (!comm.ReadExtFeature(ConstString("libraries"), ConstString(""), raw, lldberr)) return Status(0, ErrorType::eErrorTypeGeneric); if (log) log->Printf("parsing: %s", raw.c_str()); XMLDocument doc; if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) return Status(0, ErrorType::eErrorTypeGeneric); XMLNode root_element = doc.GetRootElement("library-list"); if (!root_element) return Status(); root_element.ForEachChildElementWithName( "library", [log, &list](const XMLNode &library) -> bool { LoadedModuleInfoList::LoadedModuleInfo module; llvm::StringRef name = library.GetAttributeValue("name"); module.set_name(name.str()); // The base address of a given library will be the address of its // first section. Most remotes send only one section for Windows // targets for example. const XMLNode §ion = library.FindFirstChildElementWithName("section"); llvm::StringRef address = section.GetAttributeValue("address"); module.set_base( StringConvert::ToUInt64(address.data(), LLDB_INVALID_ADDRESS, 0)); // These addresses are absolute values. module.set_base_is_offset(false); if (log) { std::string name; lldb::addr_t base = 0; bool base_is_offset; module.get_name(name); module.get_base(base); module.get_base_is_offset(base_is_offset); log->Printf("found (base:0x%08" PRIx64 "[%s], name:'%s')", base, (base_is_offset ? "offset" : "absolute"), name.c_str()); } list.add(module); return true; // Keep iterating over all "library" elements in the root // node }); if (log) log->Printf("found %" PRId32 " modules in total", (int)list.m_list.size()); } else { return Status(0, ErrorType::eErrorTypeGeneric); } return Status(); } lldb::ModuleSP ProcessGDBRemote::LoadModuleAtAddress(const FileSpec &file, lldb::addr_t link_map, lldb::addr_t base_addr, bool value_is_offset) { DynamicLoader *loader = GetDynamicLoader(); if (!loader) return nullptr; return loader->LoadModuleAtAddress(file, link_map, base_addr, value_is_offset); } size_t ProcessGDBRemote::LoadModules(LoadedModuleInfoList &module_list) { using lldb_private::process_gdb_remote::ProcessGDBRemote; // request a list of loaded libraries from GDBServer if (GetLoadedModuleList(module_list).Fail()) return 0; // get a list of all the modules ModuleList new_modules; for (LoadedModuleInfoList::LoadedModuleInfo &modInfo : module_list.m_list) { std::string mod_name; lldb::addr_t mod_base; lldb::addr_t link_map; bool mod_base_is_offset; bool valid = true; valid &= modInfo.get_name(mod_name); valid &= modInfo.get_base(mod_base); valid &= modInfo.get_base_is_offset(mod_base_is_offset); if (!valid) continue; if (!modInfo.get_link_map(link_map)) link_map = LLDB_INVALID_ADDRESS; FileSpec file(mod_name, true); lldb::ModuleSP module_sp = LoadModuleAtAddress(file, link_map, mod_base, mod_base_is_offset); if (module_sp.get()) new_modules.Append(module_sp); } if (new_modules.GetSize() > 0) { ModuleList removed_modules; Target &target = GetTarget(); ModuleList &loaded_modules = m_process->GetTarget().GetImages(); for (size_t i = 0; i < loaded_modules.GetSize(); ++i) { const lldb::ModuleSP loaded_module = loaded_modules.GetModuleAtIndex(i); bool found = false; for (size_t j = 0; j < new_modules.GetSize(); ++j) { if (new_modules.GetModuleAtIndex(j).get() == loaded_module.get()) found = true; } // The main executable will never be included in libraries-svr4, don't // remove it if (!found && loaded_module.get() != target.GetExecutableModulePointer()) { removed_modules.Append(loaded_module); } } loaded_modules.Remove(removed_modules); m_process->GetTarget().ModulesDidUnload(removed_modules, false); new_modules.ForEach([&target](const lldb::ModuleSP module_sp) -> bool { lldb_private::ObjectFile *obj = module_sp->GetObjectFile(); if (!obj) return true; if (obj->GetType() != ObjectFile::Type::eTypeExecutable) return true; lldb::ModuleSP module_copy_sp = module_sp; target.SetExecutableModule(module_copy_sp, false); return false; }); loaded_modules.AppendIfNeeded(new_modules); m_process->GetTarget().ModulesDidLoad(new_modules); } return new_modules.GetSize(); } size_t ProcessGDBRemote::LoadModules() { LoadedModuleInfoList module_list; return LoadModules(module_list); } Status ProcessGDBRemote::GetFileLoadAddress(const FileSpec &file, bool &is_loaded, lldb::addr_t &load_addr) { is_loaded = false; load_addr = LLDB_INVALID_ADDRESS; std::string file_path = file.GetPath(false); if (file_path.empty()) return Status("Empty file name specified"); StreamString packet; packet.PutCString("qFileLoadAddress:"); packet.PutCStringAsRawHex8(file_path.c_str()); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, false) != GDBRemoteCommunication::PacketResult::Success) return Status("Sending qFileLoadAddress packet failed"); if (response.IsErrorResponse()) { if (response.GetError() == 1) { // The file is not loaded into the inferior is_loaded = false; load_addr = LLDB_INVALID_ADDRESS; return Status(); } return Status( "Fetching file load address from remote server returned an error"); } if (response.IsNormalResponse()) { is_loaded = true; load_addr = response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); return Status(); } return Status( "Unknown error happened during sending the load address packet"); } void ProcessGDBRemote::ModulesDidLoad(ModuleList &module_list) { // We must call the lldb_private::Process::ModulesDidLoad () first before we // do anything Process::ModulesDidLoad(module_list); // After loading shared libraries, we can ask our remote GDB server if // it needs any symbols. m_gdb_comm.ServeSymbolLookups(this); } void ProcessGDBRemote::HandleAsyncStdout(llvm::StringRef out) { AppendSTDOUT(out.data(), out.size()); } static const char *end_delimiter = "--end--;"; static const int end_delimiter_len = 8; void ProcessGDBRemote::HandleAsyncMisc(llvm::StringRef data) { std::string input = data.str(); // '1' to move beyond 'A' if (m_partial_profile_data.length() > 0) { m_partial_profile_data.append(input); input = m_partial_profile_data; m_partial_profile_data.clear(); } size_t found, pos = 0, len = input.length(); while ((found = input.find(end_delimiter, pos)) != std::string::npos) { StringExtractorGDBRemote profileDataExtractor( input.substr(pos, found).c_str()); std::string profile_data = HarmonizeThreadIdsForProfileData(profileDataExtractor); BroadcastAsyncProfileData(profile_data); pos = found + end_delimiter_len; } if (pos < len) { // Last incomplete chunk. m_partial_profile_data = input.substr(pos); } } std::string ProcessGDBRemote::HarmonizeThreadIdsForProfileData( StringExtractorGDBRemote &profileDataExtractor) { std::map new_thread_id_to_used_usec_map; std::string output; llvm::raw_string_ostream output_stream(output); llvm::StringRef name, value; // Going to assuming thread_used_usec comes first, else bail out. while (profileDataExtractor.GetNameColonValue(name, value)) { if (name.compare("thread_used_id") == 0) { StringExtractor threadIDHexExtractor(value); uint64_t thread_id = threadIDHexExtractor.GetHexMaxU64(false, 0); bool has_used_usec = false; uint32_t curr_used_usec = 0; llvm::StringRef usec_name, usec_value; uint32_t input_file_pos = profileDataExtractor.GetFilePos(); if (profileDataExtractor.GetNameColonValue(usec_name, usec_value)) { if (usec_name.equals("thread_used_usec")) { has_used_usec = true; usec_value.getAsInteger(0, curr_used_usec); } else { // We didn't find what we want, it is probably // an older version. Bail out. profileDataExtractor.SetFilePos(input_file_pos); } } if (has_used_usec) { uint32_t prev_used_usec = 0; std::map::iterator iterator = m_thread_id_to_used_usec_map.find(thread_id); if (iterator != m_thread_id_to_used_usec_map.end()) { prev_used_usec = m_thread_id_to_used_usec_map[thread_id]; } uint32_t real_used_usec = curr_used_usec - prev_used_usec; // A good first time record is one that runs for at least 0.25 sec bool good_first_time = (prev_used_usec == 0) && (real_used_usec > 250000); bool good_subsequent_time = (prev_used_usec > 0) && ((real_used_usec > 0) || (HasAssignedIndexIDToThread(thread_id))); if (good_first_time || good_subsequent_time) { // We try to avoid doing too many index id reservation, // resulting in fast increase of index ids. output_stream << name << ":"; int32_t index_id = AssignIndexIDToThread(thread_id); output_stream << index_id << ";"; output_stream << usec_name << ":" << usec_value << ";"; } else { // Skip past 'thread_used_name'. llvm::StringRef local_name, local_value; profileDataExtractor.GetNameColonValue(local_name, local_value); } // Store current time as previous time so that they can be compared // later. new_thread_id_to_used_usec_map[thread_id] = curr_used_usec; } else { // Bail out and use old string. output_stream << name << ":" << value << ";"; } } else { output_stream << name << ":" << value << ";"; } } output_stream << end_delimiter; m_thread_id_to_used_usec_map = new_thread_id_to_used_usec_map; return output_stream.str(); } void ProcessGDBRemote::HandleStopReply() { if (GetStopID() != 0) return; if (GetID() == LLDB_INVALID_PROCESS_ID) { lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); if (pid != LLDB_INVALID_PROCESS_ID) SetID(pid); } BuildDynamicRegisterInfo(true); } static const char *const s_async_json_packet_prefix = "JSON-async:"; static StructuredData::ObjectSP ParseStructuredDataPacket(llvm::StringRef packet) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (!packet.consume_front(s_async_json_packet_prefix)) { if (log) { log->Printf( "GDBRemoteCommmunicationClientBase::%s() received $J packet " "but was not a StructuredData packet: packet starts with " "%s", __FUNCTION__, packet.slice(0, strlen(s_async_json_packet_prefix)).str().c_str()); } return StructuredData::ObjectSP(); } // This is an asynchronous JSON packet, destined for a // StructuredDataPlugin. StructuredData::ObjectSP json_sp = StructuredData::ParseJSON(packet); if (log) { if (json_sp) { StreamString json_str; json_sp->Dump(json_str); json_str.Flush(); log->Printf("ProcessGDBRemote::%s() " "received Async StructuredData packet: %s", __FUNCTION__, json_str.GetData()); } else { log->Printf("ProcessGDBRemote::%s" "() received StructuredData packet:" " parse failure", __FUNCTION__); } } return json_sp; } void ProcessGDBRemote::HandleAsyncStructuredDataPacket(llvm::StringRef data) { auto structured_data_sp = ParseStructuredDataPacket(data); if (structured_data_sp) RouteAsyncStructuredData(structured_data_sp); } class CommandObjectProcessGDBRemoteSpeedTest : public CommandObjectParsed { public: CommandObjectProcessGDBRemoteSpeedTest(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process plugin packet speed-test", "Tests packet speeds of various sizes to determine " "the performance characteristics of the GDB remote " "connection. ", NULL), m_option_group(), m_num_packets(LLDB_OPT_SET_1, false, "count", 'c', 0, eArgTypeCount, "The number of packets to send of each varying size " "(default is 1000).", 1000), m_max_send(LLDB_OPT_SET_1, false, "max-send", 's', 0, eArgTypeCount, "The maximum number of bytes to send in a packet. Sizes " "increase in powers of 2 while the size is less than or " "equal to this option value. (default 1024).", 1024), m_max_recv(LLDB_OPT_SET_1, false, "max-receive", 'r', 0, eArgTypeCount, "The maximum number of bytes to receive in a packet. Sizes " "increase in powers of 2 while the size is less than or " "equal to this option value. (default 1024).", 1024), m_json(LLDB_OPT_SET_1, false, "json", 'j', "Print the output as JSON data for easy parsing.", false, true) { m_option_group.Append(&m_num_packets, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_max_send, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_max_recv, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_json, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectProcessGDBRemoteSpeedTest() {} Options *GetOptions() override { return &m_option_group; } bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext() .GetProcessPtr(); if (process) { StreamSP output_stream_sp( m_interpreter.GetDebugger().GetAsyncOutputStream()); result.SetImmediateOutputStream(output_stream_sp); const uint32_t num_packets = (uint32_t)m_num_packets.GetOptionValue().GetCurrentValue(); const uint64_t max_send = m_max_send.GetOptionValue().GetCurrentValue(); const uint64_t max_recv = m_max_recv.GetOptionValue().GetCurrentValue(); const bool json = m_json.GetOptionValue().GetCurrentValue(); const uint64_t k_recv_amount = 4 * 1024 * 1024; // Receive amount in bytes process->GetGDBRemote().TestPacketSpeed( num_packets, max_send, max_recv, k_recv_amount, json, output_stream_sp ? *output_stream_sp : result.GetOutputStream()); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } else { result.AppendErrorWithFormat("'%s' takes no arguments", m_cmd_name.c_str()); } result.SetStatus(eReturnStatusFailed); return false; } protected: OptionGroupOptions m_option_group; OptionGroupUInt64 m_num_packets; OptionGroupUInt64 m_max_send; OptionGroupUInt64 m_max_recv; OptionGroupBoolean m_json; }; class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed { private: public: CommandObjectProcessGDBRemotePacketHistory(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process plugin packet history", "Dumps the packet history buffer. ", NULL) {} ~CommandObjectProcessGDBRemotePacketHistory() {} bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext() .GetProcessPtr(); if (process) { process->GetGDBRemote().DumpHistory(result.GetOutputStream()); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } else { result.AppendErrorWithFormat("'%s' takes no arguments", m_cmd_name.c_str()); } result.SetStatus(eReturnStatusFailed); return false; } }; class CommandObjectProcessGDBRemotePacketXferSize : public CommandObjectParsed { private: public: CommandObjectProcessGDBRemotePacketXferSize(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "process plugin packet xfer-size", "Maximum size that lldb will try to read/write one one chunk.", NULL) {} ~CommandObjectProcessGDBRemotePacketXferSize() {} bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { result.AppendErrorWithFormat("'%s' takes an argument to specify the max " "amount to be transferred when " "reading/writing", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { const char *packet_size = command.GetArgumentAtIndex(0); errno = 0; uint64_t user_specified_max = strtoul(packet_size, NULL, 10); if (errno == 0 && user_specified_max != 0) { process->SetUserSpecifiedMaxMemoryTransferSize(user_specified_max); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } result.SetStatus(eReturnStatusFailed); return false; } }; class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed { private: public: CommandObjectProcessGDBRemotePacketSend(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process plugin packet send", "Send a custom packet through the GDB remote " "protocol and print the answer. " "The packet header and footer will automatically " "be added to the packet prior to sending and " "stripped from the result.", NULL) {} ~CommandObjectProcessGDBRemotePacketSend() {} bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { result.AppendErrorWithFormat( "'%s' takes a one or more packet content arguments", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { for (size_t i = 0; i < argc; ++i) { const char *packet_cstr = command.GetArgumentAtIndex(0); bool send_async = true; StringExtractorGDBRemote response; process->GetGDBRemote().SendPacketAndWaitForResponse( packet_cstr, response, send_async); result.SetStatus(eReturnStatusSuccessFinishResult); Stream &output_strm = result.GetOutputStream(); output_strm.Printf(" packet: %s\n", packet_cstr); std::string &response_str = response.GetStringRef(); if (strstr(packet_cstr, "qGetProfileData") != NULL) { response_str = process->HarmonizeThreadIdsForProfileData(response); } if (response_str.empty()) output_strm.PutCString("response: \nerror: UNIMPLEMENTED\n"); else output_strm.Printf("response: %s\n", response.GetStringRef().c_str()); } } return true; } }; class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw { private: public: CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter) : CommandObjectRaw(interpreter, "process plugin packet monitor", "Send a qRcmd packet through the GDB remote protocol " "and print the response." "The argument passed to this command will be hex " "encoded into a valid 'qRcmd' packet, sent and the " "response will be printed.") {} ~CommandObjectProcessGDBRemotePacketMonitor() {} bool DoExecute(const char *command, CommandReturnObject &result) override { if (command == NULL || command[0] == '\0') { result.AppendErrorWithFormat("'%s' takes a command string argument", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { StreamString packet; packet.PutCString("qRcmd,"); packet.PutBytesAsRawHex8(command, strlen(command)); bool send_async = true; StringExtractorGDBRemote response; process->GetGDBRemote().SendPacketAndWaitForResponse( packet.GetString(), response, send_async); result.SetStatus(eReturnStatusSuccessFinishResult); Stream &output_strm = result.GetOutputStream(); output_strm.Printf(" packet: %s\n", packet.GetData()); const std::string &response_str = response.GetStringRef(); if (response_str.empty()) output_strm.PutCString("response: \nerror: UNIMPLEMENTED\n"); else output_strm.Printf("response: %s\n", response.GetStringRef().c_str()); } return true; } }; class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword { private: public: CommandObjectProcessGDBRemotePacket(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "process plugin packet", "Commands that deal with GDB remote packets.", NULL) { LoadSubCommand( "history", CommandObjectSP( new CommandObjectProcessGDBRemotePacketHistory(interpreter))); LoadSubCommand( "send", CommandObjectSP( new CommandObjectProcessGDBRemotePacketSend(interpreter))); LoadSubCommand( "monitor", CommandObjectSP( new CommandObjectProcessGDBRemotePacketMonitor(interpreter))); LoadSubCommand( "xfer-size", CommandObjectSP( new CommandObjectProcessGDBRemotePacketXferSize(interpreter))); LoadSubCommand("speed-test", CommandObjectSP(new CommandObjectProcessGDBRemoteSpeedTest( interpreter))); } ~CommandObjectProcessGDBRemotePacket() {} }; class CommandObjectMultiwordProcessGDBRemote : public CommandObjectMultiword { public: CommandObjectMultiwordProcessGDBRemote(CommandInterpreter &interpreter) : CommandObjectMultiword( interpreter, "process plugin", "Commands for operating on a ProcessGDBRemote process.", "process plugin []") { LoadSubCommand( "packet", CommandObjectSP(new CommandObjectProcessGDBRemotePacket(interpreter))); } ~CommandObjectMultiwordProcessGDBRemote() {} }; CommandObject *ProcessGDBRemote::GetPluginCommandObject() { if (!m_command_sp) m_command_sp.reset(new CommandObjectMultiwordProcessGDBRemote( GetTarget().GetDebugger().GetCommandInterpreter())); return m_command_sp.get(); } Index: vendor/lldb/dist/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h (revision 319149) +++ vendor/lldb/dist/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h (revision 319150) @@ -1,455 +1,471 @@ //===-- ProcessGDBRemote.h --------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_ProcessGDBRemote_h_ #define liblldb_ProcessGDBRemote_h_ // C Includes // C++ Includes #include #include #include #include #include // Other libraries and framework includes // Project includes #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Broadcaster.h" #include "lldb/Core/LoadedModuleInfoList.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/StructuredData.h" #include "lldb/Core/ThreadSafeValue.h" #include "lldb/Host/HostThread.h" #include "lldb/Target/Process.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamGDBRemote.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/StringExtractor.h" #include "lldb/Utility/StringList.h" #include "lldb/lldb-private-forward.h" #include "GDBRemoteCommunicationClient.h" #include "GDBRemoteRegisterContext.h" #include "llvm/ADT/DenseMap.h" namespace lldb_private { namespace process_gdb_remote { class ThreadGDBRemote; class ProcessGDBRemote : public Process, private GDBRemoteClientBase::ContinueDelegate { public: ProcessGDBRemote(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); ~ProcessGDBRemote() override; static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const FileSpec *crash_file_path); static void Initialize(); static void DebuggerInitialize(Debugger &debugger); static void Terminate(); static ConstString GetPluginNameStatic(); static const char *GetPluginDescriptionStatic(); //------------------------------------------------------------------ // Check if a given Process //------------------------------------------------------------------ bool CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) override; CommandObject *GetPluginCommandObject() override; //------------------------------------------------------------------ // Creating a new process, or attaching to an existing one //------------------------------------------------------------------ Status WillLaunch(Module *module) override; Status DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) override; void DidLaunch() override; Status WillAttachToProcessWithID(lldb::pid_t pid) override; Status WillAttachToProcessWithName(const char *process_name, bool wait_for_launch) override; Status DoConnectRemote(Stream *strm, llvm::StringRef remote_url) override; Status WillLaunchOrAttach(); Status DoAttachToProcessWithID(lldb::pid_t pid, const ProcessAttachInfo &attach_info) override; Status DoAttachToProcessWithName(const char *process_name, const ProcessAttachInfo &attach_info) override; void DidAttach(ArchSpec &process_arch) override; //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ ConstString GetPluginName() override; uint32_t GetPluginVersion() override; //------------------------------------------------------------------ // Process Control //------------------------------------------------------------------ Status WillResume() override; Status DoResume() override; Status DoHalt(bool &caused_stop) override; Status DoDetach(bool keep_stopped) override; bool DetachRequiresHalt() override { return true; } Status DoSignal(int signal) override; Status DoDestroy() override; void RefreshStateAfterStop() override; void SetUnixSignals(const lldb::UnixSignalsSP &signals_sp); //------------------------------------------------------------------ // Process Queries //------------------------------------------------------------------ bool IsAlive() override; lldb::addr_t GetImageInfoAddress() override; void WillPublicStop() override; //------------------------------------------------------------------ // Process Memory //------------------------------------------------------------------ size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Status &error) override; size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size, Status &error) override; lldb::addr_t DoAllocateMemory(size_t size, uint32_t permissions, Status &error) override; Status GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo ®ion_info) override; Status DoDeallocateMemory(lldb::addr_t ptr) override; //------------------------------------------------------------------ // Process STDIO //------------------------------------------------------------------ size_t PutSTDIN(const char *buf, size_t buf_size, Status &error) override; //---------------------------------------------------------------------- // Process Breakpoints //---------------------------------------------------------------------- Status EnableBreakpointSite(BreakpointSite *bp_site) override; Status DisableBreakpointSite(BreakpointSite *bp_site) override; //---------------------------------------------------------------------- // Process Watchpoints //---------------------------------------------------------------------- Status EnableWatchpoint(Watchpoint *wp, bool notify = true) override; Status DisableWatchpoint(Watchpoint *wp, bool notify = true) override; Status GetWatchpointSupportInfo(uint32_t &num) override; + + lldb::user_id_t StartTrace(const TraceOptions &options, + Status &error) override; + + Status StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) override; + + Status GetData(lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, + size_t offset = 0) override; + + Status GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id, + llvm::MutableArrayRef &buffer, + size_t offset = 0) override; + + Status GetTraceConfig(lldb::user_id_t uid, TraceOptions &options) override; Status GetWatchpointSupportInfo(uint32_t &num, bool &after) override; bool StartNoticingNewThreads() override; bool StopNoticingNewThreads() override; GDBRemoteCommunicationClient &GetGDBRemote() { return m_gdb_comm; } Status SendEventData(const char *data) override; //---------------------------------------------------------------------- // Override DidExit so we can disconnect from the remote GDB server //---------------------------------------------------------------------- void DidExit() override; void SetUserSpecifiedMaxMemoryTransferSize(uint64_t user_specified_max); bool GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, ModuleSpec &module_spec) override; void PrefetchModuleSpecs(llvm::ArrayRef module_file_specs, const llvm::Triple &triple) override; bool GetHostOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) override; size_t LoadModules(LoadedModuleInfoList &module_list) override; size_t LoadModules() override; Status GetFileLoadAddress(const FileSpec &file, bool &is_loaded, lldb::addr_t &load_addr) override; void ModulesDidLoad(ModuleList &module_list) override; StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos(lldb::addr_t image_list_address, lldb::addr_t image_count) override; Status ConfigureStructuredData(const ConstString &type_name, const StructuredData::ObjectSP &config_sp) override; StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos() override; StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos( const std::vector &load_addresses) override; StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos_sender(StructuredData::ObjectSP args); StructuredData::ObjectSP GetSharedCacheInfo() override; std::string HarmonizeThreadIdsForProfileData( StringExtractorGDBRemote &inputStringExtractor); protected: friend class ThreadGDBRemote; friend class GDBRemoteCommunicationClient; friend class GDBRemoteRegisterContext; //------------------------------------------------------------------ /// Broadcaster event bits definitions. //------------------------------------------------------------------ enum { eBroadcastBitAsyncContinue = (1 << 0), eBroadcastBitAsyncThreadShouldExit = (1 << 1), eBroadcastBitAsyncThreadDidExit = (1 << 2) }; Flags m_flags; // Process specific flags (see eFlags enums) GDBRemoteCommunicationClient m_gdb_comm; std::atomic m_debugserver_pid; std::vector m_stop_packet_stack; // The stop packet // stack replaces // the last stop // packet variable std::recursive_mutex m_last_stop_packet_mutex; GDBRemoteDynamicRegisterInfo m_register_info; Broadcaster m_async_broadcaster; lldb::ListenerSP m_async_listener_sp; HostThread m_async_thread; std::recursive_mutex m_async_thread_state_mutex; typedef std::vector tid_collection; typedef std::vector> tid_sig_collection; typedef std::map MMapMap; typedef std::map ExpeditedRegisterMap; tid_collection m_thread_ids; // Thread IDs for all threads. This list gets // updated after stopping std::vector m_thread_pcs; // PC values for all the threads. StructuredData::ObjectSP m_jstopinfo_sp; // Stop info only for any threads // that have valid stop infos StructuredData::ObjectSP m_jthreadsinfo_sp; // Full stop info, expedited // registers and memory for all // threads if "jThreadsInfo" // packet is supported tid_collection m_continue_c_tids; // 'c' for continue tid_sig_collection m_continue_C_tids; // 'C' for continue with signal tid_collection m_continue_s_tids; // 's' for step tid_sig_collection m_continue_S_tids; // 'S' for step with signal uint64_t m_max_memory_size; // The maximum number of bytes to read/write when // reading and writing memory uint64_t m_remote_stub_max_memory_size; // The maximum memory size the remote // gdb stub can handle MMapMap m_addr_to_mmap_size; lldb::BreakpointSP m_thread_create_bp_sp; bool m_waiting_for_attach; bool m_destroy_tried_resuming; lldb::CommandObjectSP m_command_sp; int64_t m_breakpoint_pc_offset; lldb::tid_t m_initial_tid; // The initial thread ID, given by stub on attach //---------------------------------------------------------------------- // Accessors //---------------------------------------------------------------------- bool IsRunning(lldb::StateType state) { return state == lldb::eStateRunning || IsStepping(state); } bool IsStepping(lldb::StateType state) { return state == lldb::eStateStepping; } bool CanResume(lldb::StateType state) { return state == lldb::eStateStopped; } bool HasExited(lldb::StateType state) { return state == lldb::eStateExited; } bool ProcessIDIsValid() const; void Clear(); Flags &GetFlags() { return m_flags; } const Flags &GetFlags() const { return m_flags; } bool UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) override; Status EstablishConnectionIfNeeded(const ProcessInfo &process_info); Status LaunchAndConnectToDebugserver(const ProcessInfo &process_info); void KillDebugserverProcess(); void BuildDynamicRegisterInfo(bool force); void SetLastStopPacket(const StringExtractorGDBRemote &response); bool ParsePythonTargetDefinition(const FileSpec &target_definition_fspec); const lldb::DataBufferSP GetAuxvData() override; StructuredData::ObjectSP GetExtendedInfoForThread(lldb::tid_t tid); void GetMaxMemorySize(); bool CalculateThreadStopInfo(ThreadGDBRemote *thread); size_t UpdateThreadPCsFromStopReplyThreadsValue(std::string &value); size_t UpdateThreadIDsFromStopReplyThreadsValue(std::string &value); bool HandleNotifyPacket(StringExtractorGDBRemote &packet); bool StartAsyncThread(); void StopAsyncThread(); static lldb::thread_result_t AsyncThread(void *arg); static bool MonitorDebugserverProcess(std::weak_ptr process_wp, lldb::pid_t pid, bool exited, int signo, int exit_status); lldb::StateType SetThreadStopInfo(StringExtractor &stop_packet); bool GetThreadStopInfoFromJSON(ThreadGDBRemote *thread, const StructuredData::ObjectSP &thread_infos_sp); lldb::ThreadSP SetThreadStopInfo(StructuredData::Dictionary *thread_dict); lldb::ThreadSP SetThreadStopInfo(lldb::tid_t tid, ExpeditedRegisterMap &expedited_register_map, uint8_t signo, const std::string &thread_name, const std::string &reason, const std::string &description, uint32_t exc_type, const std::vector &exc_data, lldb::addr_t thread_dispatch_qaddr, bool queue_vars_valid, lldb_private::LazyBool associated_with_libdispatch_queue, lldb::addr_t dispatch_queue_t, std::string &queue_name, lldb::QueueKind queue_kind, uint64_t queue_serial); void HandleStopReplySequence(); void ClearThreadIDList(); bool UpdateThreadIDList(); void DidLaunchOrAttach(ArchSpec &process_arch); Status ConnectToDebugserver(llvm::StringRef host_port); const char *GetDispatchQueueNameForThread(lldb::addr_t thread_dispatch_qaddr, std::string &dispatch_queue_name); DynamicLoader *GetDynamicLoader() override; // Query remote GDBServer for register information bool GetGDBServerRegisterInfo(ArchSpec &arch); // Query remote GDBServer for a detailed loaded library list Status GetLoadedModuleList(LoadedModuleInfoList &); lldb::ModuleSP LoadModuleAtAddress(const FileSpec &file, lldb::addr_t link_map, lldb::addr_t base_addr, bool value_is_offset); Status UpdateAutomaticSignalFiltering() override; private: //------------------------------------------------------------------ // For ProcessGDBRemote only //------------------------------------------------------------------ std::string m_partial_profile_data; std::map m_thread_id_to_used_usec_map; uint64_t m_last_signals_version = 0; static bool NewThreadNotifyBreakpointHit(void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id); //------------------------------------------------------------------ // ContinueDelegate interface //------------------------------------------------------------------ void HandleAsyncStdout(llvm::StringRef out) override; void HandleAsyncMisc(llvm::StringRef data) override; void HandleStopReply() override; void HandleAsyncStructuredDataPacket(llvm::StringRef data) override; void SetThreadPc(const lldb::ThreadSP &thread_sp, uint64_t index); using ModuleCacheKey = std::pair; // KeyInfo for the cached module spec DenseMap. // The invariant is that all real keys will have the file and architecture // set. // The empty key has an empty file and an empty arch. // The tombstone key has an invalid arch and an empty file. // The comparison and hash functions take the file name and architecture // triple into account. struct ModuleCacheInfo { static ModuleCacheKey getEmptyKey() { return ModuleCacheKey(); } static ModuleCacheKey getTombstoneKey() { return ModuleCacheKey("", "T"); } static unsigned getHashValue(const ModuleCacheKey &key) { return llvm::hash_combine(key.first, key.second); } static bool isEqual(const ModuleCacheKey &LHS, const ModuleCacheKey &RHS) { return LHS == RHS; } }; llvm::DenseMap m_cached_module_specs; DISALLOW_COPY_AND_ASSIGN(ProcessGDBRemote); }; } // namespace process_gdb_remote } // namespace lldb_private #endif // liblldb_ProcessGDBRemote_h_ Index: vendor/lldb/dist/source/Target/Process.cpp =================================================================== --- vendor/lldb/dist/source/Target/Process.cpp (revision 319149) +++ vendor/lldb/dist/source/Target/Process.cpp (revision 319150) @@ -1,6239 +1,6236 @@ //===-- Process.cpp ---------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes #include #include // Other libraries and framework includes #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/Threading.h" // Project includes #include "Plugins/Process/Utility/InferiorCallPOSIX.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Event.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Expression/DiagnosticManager.h" #include "lldb/Expression/IRDynamicChecks.h" #include "lldb/Expression/UserExpression.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/OptionParser.h" #include "lldb/Host/Pipe.h" #include "lldb/Host/Terminal.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/ABI.h" #include "lldb/Target/CPPLanguageRuntime.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/InstrumentationRuntime.h" #include "lldb/Target/JITLoader.h" #include "lldb/Target/JITLoaderList.h" #include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/MemoryHistory.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/OperatingSystem.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/StructuredDataPlugin.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/TargetList.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanBase.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/NameMatches.h" #include "lldb/Utility/SelectHelper.h" using namespace lldb; using namespace lldb_private; using namespace std::chrono; // Comment out line below to disable memory caching, overriding the process // setting target.process.disable-memory-cache #define ENABLE_MEMORY_CACHING #ifdef ENABLE_MEMORY_CACHING #define DISABLE_MEM_CACHE_DEFAULT false #else #define DISABLE_MEM_CACHE_DEFAULT true #endif class ProcessOptionValueProperties : public OptionValueProperties { public: ProcessOptionValueProperties(const ConstString &name) : OptionValueProperties(name) {} // This constructor is used when creating ProcessOptionValueProperties when it // is part of a new lldb_private::Process instance. It will copy all current // global property values as needed ProcessOptionValueProperties(ProcessProperties *global_properties) : OptionValueProperties(*global_properties->GetValueProperties()) {} const Property *GetPropertyAtIndex(const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const override { // When getting the value for a key from the process options, we will always // try and grab the setting from the current process if there is one. Else // we just // use the one from this instance. if (exe_ctx) { Process *process = exe_ctx->GetProcessPtr(); if (process) { ProcessOptionValueProperties *instance_properties = static_cast( process->GetValueProperties().get()); if (this != instance_properties) return instance_properties->ProtectedGetPropertyAtIndex(idx); } } return ProtectedGetPropertyAtIndex(idx); } }; static PropertyDefinition g_properties[] = { {"disable-memory-cache", OptionValue::eTypeBoolean, false, DISABLE_MEM_CACHE_DEFAULT, nullptr, nullptr, "Disable reading and caching of memory in fixed-size units."}, {"extra-startup-command", OptionValue::eTypeArray, false, OptionValue::eTypeString, nullptr, nullptr, "A list containing extra commands understood by the particular process " "plugin used. " "For instance, to turn on debugserver logging set this to " "\"QSetLogging:bitmask=LOG_DEFAULT;\""}, {"ignore-breakpoints-in-expressions", OptionValue::eTypeBoolean, true, true, nullptr, nullptr, "If true, breakpoints will be ignored during expression evaluation."}, {"unwind-on-error-in-expressions", OptionValue::eTypeBoolean, true, true, nullptr, nullptr, "If true, errors in expression evaluation will unwind " "the stack back to the state before the call."}, {"python-os-plugin-path", OptionValue::eTypeFileSpec, false, true, nullptr, nullptr, "A path to a python OS plug-in module file that contains a " "OperatingSystemPlugIn class."}, {"stop-on-sharedlibrary-events", OptionValue::eTypeBoolean, true, false, nullptr, nullptr, "If true, stop when a shared library is loaded or unloaded."}, {"detach-keeps-stopped", OptionValue::eTypeBoolean, true, false, nullptr, nullptr, "If true, detach will attempt to keep the process stopped."}, {"memory-cache-line-size", OptionValue::eTypeUInt64, false, 512, nullptr, nullptr, "The memory cache line size"}, {"optimization-warnings", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, "If true, warn when stopped in code that is optimized where " "stepping and variable availability may not behave as expected."}, {nullptr, OptionValue::eTypeInvalid, false, 0, nullptr, nullptr, nullptr}}; enum { ePropertyDisableMemCache, ePropertyExtraStartCommand, ePropertyIgnoreBreakpointsInExpressions, ePropertyUnwindOnErrorInExpressions, ePropertyPythonOSPluginPath, ePropertyStopOnSharedLibraryEvents, ePropertyDetachKeepsStopped, ePropertyMemCacheLineSize, ePropertyWarningOptimization }; ProcessProperties::ProcessProperties(lldb_private::Process *process) : Properties(), m_process(process) // Can be nullptr for global ProcessProperties { if (process == nullptr) { // Global process properties, set them up one time m_collection_sp.reset( new ProcessOptionValueProperties(ConstString("process"))); m_collection_sp->Initialize(g_properties); m_collection_sp->AppendProperty( ConstString("thread"), ConstString("Settings specific to threads."), true, Thread::GetGlobalProperties()->GetValueProperties()); } else { m_collection_sp.reset( new ProcessOptionValueProperties(Process::GetGlobalProperties().get())); m_collection_sp->SetValueChangedCallback( ePropertyPythonOSPluginPath, ProcessProperties::OptionValueChangedCallback, this); } } ProcessProperties::~ProcessProperties() = default; void ProcessProperties::OptionValueChangedCallback(void *baton, OptionValue *option_value) { ProcessProperties *properties = (ProcessProperties *)baton; if (properties->m_process) properties->m_process->LoadOperatingSystemPlugin(true); } bool ProcessProperties::GetDisableMemoryCache() const { const uint32_t idx = ePropertyDisableMemCache; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } uint64_t ProcessProperties::GetMemoryCacheLineSize() const { const uint32_t idx = ePropertyMemCacheLineSize; return m_collection_sp->GetPropertyAtIndexAsUInt64( nullptr, idx, g_properties[idx].default_uint_value); } Args ProcessProperties::GetExtraStartupCommands() const { Args args; const uint32_t idx = ePropertyExtraStartCommand; m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, args); return args; } void ProcessProperties::SetExtraStartupCommands(const Args &args) { const uint32_t idx = ePropertyExtraStartCommand; m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, args); } FileSpec ProcessProperties::GetPythonOSPluginPath() const { const uint32_t idx = ePropertyPythonOSPluginPath; return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); } void ProcessProperties::SetPythonOSPluginPath(const FileSpec &file) { const uint32_t idx = ePropertyPythonOSPluginPath; m_collection_sp->SetPropertyAtIndexAsFileSpec(nullptr, idx, file); } bool ProcessProperties::GetIgnoreBreakpointsInExpressions() const { const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } void ProcessProperties::SetIgnoreBreakpointsInExpressions(bool ignore) { const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, ignore); } bool ProcessProperties::GetUnwindOnErrorInExpressions() const { const uint32_t idx = ePropertyUnwindOnErrorInExpressions; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } void ProcessProperties::SetUnwindOnErrorInExpressions(bool ignore) { const uint32_t idx = ePropertyUnwindOnErrorInExpressions; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, ignore); } bool ProcessProperties::GetStopOnSharedLibraryEvents() const { const uint32_t idx = ePropertyStopOnSharedLibraryEvents; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } void ProcessProperties::SetStopOnSharedLibraryEvents(bool stop) { const uint32_t idx = ePropertyStopOnSharedLibraryEvents; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, stop); } bool ProcessProperties::GetDetachKeepsStopped() const { const uint32_t idx = ePropertyDetachKeepsStopped; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } void ProcessProperties::SetDetachKeepsStopped(bool stop) { const uint32_t idx = ePropertyDetachKeepsStopped; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, stop); } bool ProcessProperties::GetWarningsOptimization() const { const uint32_t idx = ePropertyWarningOptimization; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } void ProcessInstanceInfo::Dump(Stream &s, Platform *platform) const { const char *cstr; if (m_pid != LLDB_INVALID_PROCESS_ID) s.Printf(" pid = %" PRIu64 "\n", m_pid); if (m_parent_pid != LLDB_INVALID_PROCESS_ID) s.Printf(" parent = %" PRIu64 "\n", m_parent_pid); if (m_executable) { s.Printf(" name = %s\n", m_executable.GetFilename().GetCString()); s.PutCString(" file = "); m_executable.Dump(&s); s.EOL(); } const uint32_t argc = m_arguments.GetArgumentCount(); if (argc > 0) { for (uint32_t i = 0; i < argc; i++) { const char *arg = m_arguments.GetArgumentAtIndex(i); if (i < 10) s.Printf(" arg[%u] = %s\n", i, arg); else s.Printf("arg[%u] = %s\n", i, arg); } } const uint32_t envc = m_environment.GetArgumentCount(); if (envc > 0) { for (uint32_t i = 0; i < envc; i++) { const char *env = m_environment.GetArgumentAtIndex(i); if (i < 10) s.Printf(" env[%u] = %s\n", i, env); else s.Printf("env[%u] = %s\n", i, env); } } if (m_arch.IsValid()) { s.Printf(" arch = "); m_arch.DumpTriple(s); s.EOL(); } if (m_uid != UINT32_MAX) { cstr = platform->GetUserName(m_uid); s.Printf(" uid = %-5u (%s)\n", m_uid, cstr ? cstr : ""); } if (m_gid != UINT32_MAX) { cstr = platform->GetGroupName(m_gid); s.Printf(" gid = %-5u (%s)\n", m_gid, cstr ? cstr : ""); } if (m_euid != UINT32_MAX) { cstr = platform->GetUserName(m_euid); s.Printf(" euid = %-5u (%s)\n", m_euid, cstr ? cstr : ""); } if (m_egid != UINT32_MAX) { cstr = platform->GetGroupName(m_egid); s.Printf(" egid = %-5u (%s)\n", m_egid, cstr ? cstr : ""); } } void ProcessInstanceInfo::DumpTableHeader(Stream &s, Platform *platform, bool show_args, bool verbose) { const char *label; if (show_args || verbose) label = "ARGUMENTS"; else label = "NAME"; if (verbose) { s.Printf("PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE " " %s\n", label); s.PutCString("====== ====== ========== ========== ========== ========== " "======================== ============================\n"); } else { s.Printf("PID PARENT USER TRIPLE %s\n", label); s.PutCString("====== ====== ========== ======================== " "============================\n"); } } void ProcessInstanceInfo::DumpAsTableRow(Stream &s, Platform *platform, bool show_args, bool verbose) const { if (m_pid != LLDB_INVALID_PROCESS_ID) { const char *cstr; s.Printf("%-6" PRIu64 " %-6" PRIu64 " ", m_pid, m_parent_pid); StreamString arch_strm; if (m_arch.IsValid()) m_arch.DumpTriple(arch_strm); if (verbose) { cstr = platform->GetUserName(m_uid); if (cstr && cstr[0]) // Watch for empty string that indicates lookup failed s.Printf("%-10s ", cstr); else s.Printf("%-10u ", m_uid); cstr = platform->GetGroupName(m_gid); if (cstr && cstr[0]) // Watch for empty string that indicates lookup failed s.Printf("%-10s ", cstr); else s.Printf("%-10u ", m_gid); cstr = platform->GetUserName(m_euid); if (cstr && cstr[0]) // Watch for empty string that indicates lookup failed s.Printf("%-10s ", cstr); else s.Printf("%-10u ", m_euid); cstr = platform->GetGroupName(m_egid); if (cstr && cstr[0]) // Watch for empty string that indicates lookup failed s.Printf("%-10s ", cstr); else s.Printf("%-10u ", m_egid); s.Printf("%-24s ", arch_strm.GetData()); } else { s.Printf("%-10s %-24s ", platform->GetUserName(m_euid), arch_strm.GetData()); } if (verbose || show_args) { const uint32_t argc = m_arguments.GetArgumentCount(); if (argc > 0) { for (uint32_t i = 0; i < argc; i++) { if (i > 0) s.PutChar(' '); s.PutCString(m_arguments.GetArgumentAtIndex(i)); } } } else { s.PutCString(GetName()); } s.EOL(); } } Status ProcessLaunchCommandOptions::SetOptionValue( uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 's': // Stop at program entry point launch_info.GetFlags().Set(eLaunchFlagStopAtEntry); break; case 'i': // STDIN for read only { FileAction action; if (action.Open(STDIN_FILENO, FileSpec{option_arg, false}, true, false)) launch_info.AppendFileAction(action); break; } case 'o': // Open STDOUT for write only { FileAction action; if (action.Open(STDOUT_FILENO, FileSpec{option_arg, false}, false, true)) launch_info.AppendFileAction(action); break; } case 'e': // STDERR for write only { FileAction action; if (action.Open(STDERR_FILENO, FileSpec{option_arg, false}, false, true)) launch_info.AppendFileAction(action); break; } case 'p': // Process plug-in name launch_info.SetProcessPluginName(option_arg); break; case 'n': // Disable STDIO { FileAction action; const FileSpec dev_null{FileSystem::DEV_NULL, false}; if (action.Open(STDIN_FILENO, dev_null, true, false)) launch_info.AppendFileAction(action); if (action.Open(STDOUT_FILENO, dev_null, false, true)) launch_info.AppendFileAction(action); if (action.Open(STDERR_FILENO, dev_null, false, true)) launch_info.AppendFileAction(action); break; } case 'w': launch_info.SetWorkingDirectory(FileSpec{option_arg, false}); break; case 't': // Open process in new terminal window launch_info.GetFlags().Set(eLaunchFlagLaunchInTTY); break; case 'a': { TargetSP target_sp = execution_context ? execution_context->GetTargetSP() : TargetSP(); PlatformSP platform_sp = target_sp ? target_sp->GetPlatform() : PlatformSP(); if (!launch_info.GetArchitecture().SetTriple(option_arg, platform_sp.get())) launch_info.GetArchitecture().SetTriple(option_arg); } break; case 'A': // Disable ASLR. { bool success; const bool disable_aslr_arg = Args::StringToBoolean(option_arg, true, &success); if (success) disable_aslr = disable_aslr_arg ? eLazyBoolYes : eLazyBoolNo; else error.SetErrorStringWithFormat( "Invalid boolean value for disable-aslr option: '%s'", option_arg.empty() ? "" : option_arg.str().c_str()); break; } case 'X': // shell expand args. { bool success; const bool expand_args = Args::StringToBoolean(option_arg, true, &success); if (success) launch_info.SetShellExpandArguments(expand_args); else error.SetErrorStringWithFormat( "Invalid boolean value for shell-expand-args option: '%s'", option_arg.empty() ? "" : option_arg.str().c_str()); break; } case 'c': if (!option_arg.empty()) launch_info.SetShell(FileSpec(option_arg, false)); else launch_info.SetShell(HostInfo::GetDefaultShell()); break; case 'v': launch_info.GetEnvironmentEntries().AppendArgument(option_arg); break; default: error.SetErrorStringWithFormat("unrecognized short option character '%c'", short_option); break; } return error; } static OptionDefinition g_process_launch_options[] = { {LLDB_OPT_SET_ALL, false, "stop-at-entry", 's', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Stop at the entry point of the program when launching a process."}, {LLDB_OPT_SET_ALL, false, "disable-aslr", 'A', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "Set whether to disable address space layout randomization when launching " "a process."}, {LLDB_OPT_SET_ALL, false, "plugin", 'p', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePlugin, "Name of the process plugin you want to use."}, {LLDB_OPT_SET_ALL, false, "working-dir", 'w', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeDirectoryName, "Set the current working directory to when running the inferior."}, {LLDB_OPT_SET_ALL, false, "arch", 'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeArchitecture, "Set the architecture for the process to launch when ambiguous."}, {LLDB_OPT_SET_ALL, false, "environment", 'v', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeNone, "Specify an environment variable name/value string (--environment " "NAME=VALUE). Can be specified multiple times for subsequent environment " "entries."}, {LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "shell", 'c', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeFilename, "Run the process in a shell (not supported on all platforms)."}, {LLDB_OPT_SET_1, false, "stdin", 'i', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFilename, "Redirect stdin for the process to ."}, {LLDB_OPT_SET_1, false, "stdout", 'o', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFilename, "Redirect stdout for the process to ."}, {LLDB_OPT_SET_1, false, "stderr", 'e', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFilename, "Redirect stderr for the process to ."}, {LLDB_OPT_SET_2, false, "tty", 't', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Start the process in a terminal (not supported on all platforms)."}, {LLDB_OPT_SET_3, false, "no-stdio", 'n', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Do not set up for terminal I/O to go to running process."}, {LLDB_OPT_SET_4, false, "shell-expand-args", 'X', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "Set whether to shell expand arguments to the process when launching."}, }; llvm::ArrayRef ProcessLaunchCommandOptions::GetDefinitions() { return llvm::makeArrayRef(g_process_launch_options); } bool ProcessInstanceInfoMatch::NameMatches(const char *process_name) const { if (m_name_match_type == NameMatch::Ignore || process_name == nullptr) return true; const char *match_name = m_match_info.GetName(); if (!match_name) return true; return lldb_private::NameMatches(process_name, m_name_match_type, match_name); } bool ProcessInstanceInfoMatch::Matches( const ProcessInstanceInfo &proc_info) const { if (!NameMatches(proc_info.GetName())) return false; if (m_match_info.ProcessIDIsValid() && m_match_info.GetProcessID() != proc_info.GetProcessID()) return false; if (m_match_info.ParentProcessIDIsValid() && m_match_info.GetParentProcessID() != proc_info.GetParentProcessID()) return false; if (m_match_info.UserIDIsValid() && m_match_info.GetUserID() != proc_info.GetUserID()) return false; if (m_match_info.GroupIDIsValid() && m_match_info.GetGroupID() != proc_info.GetGroupID()) return false; if (m_match_info.EffectiveUserIDIsValid() && m_match_info.GetEffectiveUserID() != proc_info.GetEffectiveUserID()) return false; if (m_match_info.EffectiveGroupIDIsValid() && m_match_info.GetEffectiveGroupID() != proc_info.GetEffectiveGroupID()) return false; if (m_match_info.GetArchitecture().IsValid() && !m_match_info.GetArchitecture().IsCompatibleMatch( proc_info.GetArchitecture())) return false; return true; } bool ProcessInstanceInfoMatch::MatchAllProcesses() const { if (m_name_match_type != NameMatch::Ignore) return false; if (m_match_info.ProcessIDIsValid()) return false; if (m_match_info.ParentProcessIDIsValid()) return false; if (m_match_info.UserIDIsValid()) return false; if (m_match_info.GroupIDIsValid()) return false; if (m_match_info.EffectiveUserIDIsValid()) return false; if (m_match_info.EffectiveGroupIDIsValid()) return false; if (m_match_info.GetArchitecture().IsValid()) return false; if (m_match_all_users) return false; return true; } void ProcessInstanceInfoMatch::Clear() { m_match_info.Clear(); m_name_match_type = NameMatch::Ignore; m_match_all_users = false; } ProcessSP Process::FindPlugin(lldb::TargetSP target_sp, llvm::StringRef plugin_name, ListenerSP listener_sp, const FileSpec *crash_file_path) { static uint32_t g_process_unique_id = 0; ProcessSP process_sp; ProcessCreateInstance create_callback = nullptr; if (!plugin_name.empty()) { ConstString const_plugin_name(plugin_name); create_callback = PluginManager::GetProcessCreateCallbackForPluginName(const_plugin_name); if (create_callback) { process_sp = create_callback(target_sp, listener_sp, crash_file_path); if (process_sp) { if (process_sp->CanDebug(target_sp, true)) { process_sp->m_process_unique_id = ++g_process_unique_id; } else process_sp.reset(); } } } else { for (uint32_t idx = 0; (create_callback = PluginManager::GetProcessCreateCallbackAtIndex(idx)) != nullptr; ++idx) { process_sp = create_callback(target_sp, listener_sp, crash_file_path); if (process_sp) { if (process_sp->CanDebug(target_sp, false)) { process_sp->m_process_unique_id = ++g_process_unique_id; break; } else process_sp.reset(); } } } return process_sp; } ConstString &Process::GetStaticBroadcasterClass() { static ConstString class_name("lldb.process"); return class_name; } Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp) : Process(target_sp, listener_sp, UnixSignals::Create(HostInfo::GetArchitecture())) { // This constructor just delegates to the full Process constructor, // defaulting to using the Host's UnixSignals. } Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp, const UnixSignalsSP &unix_signals_sp) : ProcessProperties(this), UserID(LLDB_INVALID_PROCESS_ID), Broadcaster((target_sp->GetDebugger().GetBroadcasterManager()), Process::GetStaticBroadcasterClass().AsCString()), m_target_sp(target_sp), m_public_state(eStateUnloaded), m_private_state(eStateUnloaded), m_private_state_broadcaster(nullptr, "lldb.process.internal_state_broadcaster"), m_private_state_control_broadcaster( nullptr, "lldb.process.internal_state_control_broadcaster"), m_private_state_listener_sp( Listener::MakeListener("lldb.process.internal_state_listener")), m_mod_id(), m_process_unique_id(0), m_thread_index_id(0), m_thread_id_to_index_id_map(), m_exit_status(-1), m_exit_string(), m_exit_status_mutex(), m_thread_mutex(), m_thread_list_real(this), m_thread_list(this), m_extended_thread_list(this), m_extended_thread_stop_id(0), m_queue_list(this), m_queue_list_stop_id(0), m_notifications(), m_image_tokens(), m_listener_sp(listener_sp), m_breakpoint_site_list(), m_dynamic_checkers_ap(), m_unix_signals_sp(unix_signals_sp), m_abi_sp(), m_process_input_reader(), m_stdio_communication("process.stdio"), m_stdio_communication_mutex(), m_stdin_forward(false), m_stdout_data(), m_stderr_data(), m_profile_data_comm_mutex(), m_profile_data(), m_iohandler_sync(0), m_memory_cache(*this), m_allocated_memory_cache(*this), m_should_detach(false), m_next_event_action_ap(), m_public_run_lock(), m_private_run_lock(), m_stop_info_override_callback(nullptr), m_finalizing(false), m_finalize_called(false), m_clear_thread_plans_on_stop(false), m_force_next_event_delivery(false), m_last_broadcast_state(eStateInvalid), m_destroy_in_process(false), m_can_interpret_function_calls(false), m_warnings_issued(), m_run_thread_plan_lock(), m_can_jit(eCanJITDontKnow) { CheckInWithManager(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); if (log) log->Printf("%p Process::Process()", static_cast(this)); if (!m_unix_signals_sp) m_unix_signals_sp = std::make_shared(); SetEventName(eBroadcastBitStateChanged, "state-changed"); SetEventName(eBroadcastBitInterrupt, "interrupt"); SetEventName(eBroadcastBitSTDOUT, "stdout-available"); SetEventName(eBroadcastBitSTDERR, "stderr-available"); SetEventName(eBroadcastBitProfileData, "profile-data-available"); SetEventName(eBroadcastBitStructuredData, "structured-data-available"); m_private_state_control_broadcaster.SetEventName( eBroadcastInternalStateControlStop, "control-stop"); m_private_state_control_broadcaster.SetEventName( eBroadcastInternalStateControlPause, "control-pause"); m_private_state_control_broadcaster.SetEventName( eBroadcastInternalStateControlResume, "control-resume"); m_listener_sp->StartListeningForEvents( this, eBroadcastBitStateChanged | eBroadcastBitInterrupt | eBroadcastBitSTDOUT | eBroadcastBitSTDERR | eBroadcastBitProfileData | eBroadcastBitStructuredData); m_private_state_listener_sp->StartListeningForEvents( &m_private_state_broadcaster, eBroadcastBitStateChanged | eBroadcastBitInterrupt); m_private_state_listener_sp->StartListeningForEvents( &m_private_state_control_broadcaster, eBroadcastInternalStateControlStop | eBroadcastInternalStateControlPause | eBroadcastInternalStateControlResume); // We need something valid here, even if just the default UnixSignalsSP. assert(m_unix_signals_sp && "null m_unix_signals_sp after initialization"); // Allow the platform to override the default cache line size OptionValueSP value_sp = m_collection_sp ->GetPropertyAtIndex(nullptr, true, ePropertyMemCacheLineSize) ->GetValue(); uint32_t platform_cache_line_size = target_sp->GetPlatform()->GetDefaultMemoryCacheLineSize(); if (!value_sp->OptionWasSet() && platform_cache_line_size != 0) value_sp->SetUInt64Value(platform_cache_line_size); } Process::~Process() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); if (log) log->Printf("%p Process::~Process()", static_cast(this)); StopPrivateStateThread(); // ThreadList::Clear() will try to acquire this process's mutex, so // explicitly clear the thread list here to ensure that the mutex // is not destroyed before the thread list. m_thread_list.Clear(); } const ProcessPropertiesSP &Process::GetGlobalProperties() { // NOTE: intentional leak so we don't crash if global destructor chain gets // called as other threads still use the result of this function static ProcessPropertiesSP *g_settings_sp_ptr = new ProcessPropertiesSP(new ProcessProperties(nullptr)); return *g_settings_sp_ptr; } void Process::Finalize() { m_finalizing = true; // Destroy this process if needed switch (GetPrivateState()) { case eStateConnected: case eStateAttaching: case eStateLaunching: case eStateStopped: case eStateRunning: case eStateStepping: case eStateCrashed: case eStateSuspended: Destroy(false); break; case eStateInvalid: case eStateUnloaded: case eStateDetached: case eStateExited: break; } // Clear our broadcaster before we proceed with destroying Broadcaster::Clear(); // Do any cleanup needed prior to being destructed... Subclasses // that override this method should call this superclass method as well. // We need to destroy the loader before the derived Process class gets // destroyed // since it is very likely that undoing the loader will require access to the // real process. m_dynamic_checkers_ap.reset(); m_abi_sp.reset(); m_os_ap.reset(); m_system_runtime_ap.reset(); m_dyld_ap.reset(); m_jit_loaders_ap.reset(); m_thread_list_real.Destroy(); m_thread_list.Destroy(); m_extended_thread_list.Destroy(); m_queue_list.Clear(); m_queue_list_stop_id = 0; std::vector empty_notifications; m_notifications.swap(empty_notifications); m_image_tokens.clear(); m_memory_cache.Clear(); m_allocated_memory_cache.Clear(); m_language_runtimes.clear(); m_instrumentation_runtimes.clear(); m_next_event_action_ap.reset(); m_stop_info_override_callback = nullptr; // Clear the last natural stop ID since it has a strong // reference to this process m_mod_id.SetStopEventForLastNaturalStopID(EventSP()); //#ifdef LLDB_CONFIGURATION_DEBUG // StreamFile s(stdout, false); // EventSP event_sp; // while (m_private_state_listener_sp->GetNextEvent(event_sp)) // { // event_sp->Dump (&s); // s.EOL(); // } //#endif // We have to be very careful here as the m_private_state_listener might // contain events that have ProcessSP values in them which can keep this // process around forever. These events need to be cleared out. m_private_state_listener_sp->Clear(); m_public_run_lock.TrySetRunning(); // This will do nothing if already locked m_public_run_lock.SetStopped(); m_private_run_lock.TrySetRunning(); // This will do nothing if already locked m_private_run_lock.SetStopped(); m_structured_data_plugin_map.clear(); m_finalize_called = true; } void Process::RegisterNotificationCallbacks(const Notifications &callbacks) { m_notifications.push_back(callbacks); if (callbacks.initialize != nullptr) callbacks.initialize(callbacks.baton, this); } bool Process::UnregisterNotificationCallbacks(const Notifications &callbacks) { std::vector::iterator pos, end = m_notifications.end(); for (pos = m_notifications.begin(); pos != end; ++pos) { if (pos->baton == callbacks.baton && pos->initialize == callbacks.initialize && pos->process_state_changed == callbacks.process_state_changed) { m_notifications.erase(pos); return true; } } return false; } void Process::SynchronouslyNotifyStateChanged(StateType state) { std::vector::iterator notification_pos, notification_end = m_notifications.end(); for (notification_pos = m_notifications.begin(); notification_pos != notification_end; ++notification_pos) { if (notification_pos->process_state_changed) notification_pos->process_state_changed(notification_pos->baton, this, state); } } // FIXME: We need to do some work on events before the general Listener sees // them. // For instance if we are continuing from a breakpoint, we need to ensure that // we do // the little "insert real insn, step & stop" trick. But we can't do that when // the // event is delivered by the broadcaster - since that is done on the thread that // is // waiting for new events, so if we needed more than one event for our handling, // we would // stall. So instead we do it when we fetch the event off of the queue. // StateType Process::GetNextEvent(EventSP &event_sp) { StateType state = eStateInvalid; if (m_listener_sp->GetEventForBroadcaster(this, event_sp, std::chrono::seconds(0)) && event_sp) state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); return state; } void Process::SyncIOHandler(uint32_t iohandler_id, uint64_t timeout_msec) { // don't sync (potentially context switch) in case where there is no process // IO if (!m_process_input_reader) return; uint32_t new_iohandler_id = 0; m_iohandler_sync.WaitForValueNotEqualTo( iohandler_id, new_iohandler_id, std::chrono::milliseconds(timeout_msec)); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::%s waited for m_iohandler_sync to change from %u, " "new value is %u", __FUNCTION__, iohandler_id, new_iohandler_id); } StateType Process::WaitForProcessToStop(const Timeout &timeout, EventSP *event_sp_ptr, bool wait_always, ListenerSP hijack_listener_sp, Stream *stream, bool use_run_lock) { // We can't just wait for a "stopped" event, because the stopped event may // have restarted the target. // We have to actually check each event, and in the case of a stopped event // check the restarted flag // on the event. if (event_sp_ptr) event_sp_ptr->reset(); StateType state = GetState(); // If we are exited or detached, we won't ever get back to any // other valid state... if (state == eStateDetached || state == eStateExited) return state; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); LLDB_LOG(log, "timeout = {0}", timeout); if (!wait_always && StateIsStoppedState(state, true) && StateIsStoppedState(GetPrivateState(), true)) { if (log) log->Printf("Process::%s returning without waiting for events; process " "private and public states are already 'stopped'.", __FUNCTION__); // We need to toggle the run lock as this won't get done in // SetPublicState() if the process is hijacked. if (hijack_listener_sp && use_run_lock) m_public_run_lock.SetStopped(); return state; } while (state != eStateInvalid) { EventSP event_sp; state = GetStateChangedEvents(event_sp, timeout, hijack_listener_sp); if (event_sp_ptr && event_sp) *event_sp_ptr = event_sp; bool pop_process_io_handler = (hijack_listener_sp.get() != nullptr); Process::HandleProcessStateChangedEvent(event_sp, stream, pop_process_io_handler); switch (state) { case eStateCrashed: case eStateDetached: case eStateExited: case eStateUnloaded: // We need to toggle the run lock as this won't get done in // SetPublicState() if the process is hijacked. if (hijack_listener_sp && use_run_lock) m_public_run_lock.SetStopped(); return state; case eStateStopped: if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) continue; else { // We need to toggle the run lock as this won't get done in // SetPublicState() if the process is hijacked. if (hijack_listener_sp && use_run_lock) m_public_run_lock.SetStopped(); return state; } default: continue; } } return state; } bool Process::HandleProcessStateChangedEvent(const EventSP &event_sp, Stream *stream, bool &pop_process_io_handler) { const bool handle_pop = pop_process_io_handler; pop_process_io_handler = false; ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event_sp.get()); if (!process_sp) return false; StateType event_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); if (event_state == eStateInvalid) return false; switch (event_state) { case eStateInvalid: case eStateUnloaded: case eStateAttaching: case eStateLaunching: case eStateStepping: case eStateDetached: if (stream) stream->Printf("Process %" PRIu64 " %s\n", process_sp->GetID(), StateAsCString(event_state)); if (event_state == eStateDetached) pop_process_io_handler = true; break; case eStateConnected: case eStateRunning: // Don't be chatty when we run... break; case eStateExited: if (stream) process_sp->GetStatus(*stream); pop_process_io_handler = true; break; case eStateStopped: case eStateCrashed: case eStateSuspended: // Make sure the program hasn't been auto-restarted: if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) { if (stream) { size_t num_reasons = Process::ProcessEventData::GetNumRestartedReasons(event_sp.get()); if (num_reasons > 0) { // FIXME: Do we want to report this, or would that just be annoyingly // chatty? if (num_reasons == 1) { const char *reason = Process::ProcessEventData::GetRestartedReasonAtIndex( event_sp.get(), 0); stream->Printf("Process %" PRIu64 " stopped and restarted: %s\n", process_sp->GetID(), reason ? reason : ""); } else { stream->Printf("Process %" PRIu64 " stopped and restarted, reasons:\n", process_sp->GetID()); for (size_t i = 0; i < num_reasons; i++) { const char *reason = Process::ProcessEventData::GetRestartedReasonAtIndex( event_sp.get(), i); stream->Printf("\t%s\n", reason ? reason : ""); } } } } } else { StopInfoSP curr_thread_stop_info_sp; // Lock the thread list so it doesn't change on us, this is the scope for // the locker: { ThreadList &thread_list = process_sp->GetThreadList(); std::lock_guard guard(thread_list.GetMutex()); ThreadSP curr_thread(thread_list.GetSelectedThread()); ThreadSP thread; StopReason curr_thread_stop_reason = eStopReasonInvalid; if (curr_thread) { curr_thread_stop_reason = curr_thread->GetStopReason(); curr_thread_stop_info_sp = curr_thread->GetStopInfo(); } if (!curr_thread || !curr_thread->IsValid() || curr_thread_stop_reason == eStopReasonInvalid || curr_thread_stop_reason == eStopReasonNone) { // Prefer a thread that has just completed its plan over another // thread as current thread. ThreadSP plan_thread; ThreadSP other_thread; const size_t num_threads = thread_list.GetSize(); size_t i; for (i = 0; i < num_threads; ++i) { thread = thread_list.GetThreadAtIndex(i); StopReason thread_stop_reason = thread->GetStopReason(); switch (thread_stop_reason) { case eStopReasonInvalid: case eStopReasonNone: break; case eStopReasonSignal: { // Don't select a signal thread if we weren't going to stop at // that // signal. We have to have had another reason for stopping here, // and // the user doesn't want to see this thread. uint64_t signo = thread->GetStopInfo()->GetValue(); if (process_sp->GetUnixSignals()->GetShouldStop(signo)) { if (!other_thread) other_thread = thread; } break; } case eStopReasonTrace: case eStopReasonBreakpoint: case eStopReasonWatchpoint: case eStopReasonException: case eStopReasonExec: case eStopReasonThreadExiting: case eStopReasonInstrumentation: if (!other_thread) other_thread = thread; break; case eStopReasonPlanComplete: if (!plan_thread) plan_thread = thread; break; } } if (plan_thread) thread_list.SetSelectedThreadByID(plan_thread->GetID()); else if (other_thread) thread_list.SetSelectedThreadByID(other_thread->GetID()); else { if (curr_thread && curr_thread->IsValid()) thread = curr_thread; else thread = thread_list.GetThreadAtIndex(0); if (thread) thread_list.SetSelectedThreadByID(thread->GetID()); } } } // Drop the ThreadList mutex by here, since GetThreadStatus below might // have to run code, // e.g. for Data formatters, and if we hold the ThreadList mutex, then the // process is going to // have a hard time restarting the process. if (stream) { Debugger &debugger = process_sp->GetTarget().GetDebugger(); if (debugger.GetTargetList().GetSelectedTarget().get() == &process_sp->GetTarget()) { const bool only_threads_with_stop_reason = true; const uint32_t start_frame = 0; const uint32_t num_frames = 1; const uint32_t num_frames_with_source = 1; const bool stop_format = true; process_sp->GetStatus(*stream); process_sp->GetThreadStatus(*stream, only_threads_with_stop_reason, start_frame, num_frames, num_frames_with_source, stop_format); if (curr_thread_stop_info_sp) { lldb::addr_t crashing_address; ValueObjectSP valobj_sp = StopInfo::GetCrashingDereference( curr_thread_stop_info_sp, &crashing_address); if (valobj_sp) { const bool qualify_cxx_base_classes = false; const ValueObject::GetExpressionPathFormat format = ValueObject::GetExpressionPathFormat:: eGetExpressionPathFormatHonorPointers; stream->PutCString("Likely cause: "); valobj_sp->GetExpressionPath(*stream, qualify_cxx_base_classes, format); stream->Printf(" accessed 0x%" PRIx64 "\n", crashing_address); } } } else { uint32_t target_idx = debugger.GetTargetList().GetIndexOfTarget( process_sp->GetTarget().shared_from_this()); if (target_idx != UINT32_MAX) stream->Printf("Target %d: (", target_idx); else stream->Printf("Target : ("); process_sp->GetTarget().Dump(stream, eDescriptionLevelBrief); stream->Printf(") stopped.\n"); } } // Pop the process IO handler pop_process_io_handler = true; } break; } if (handle_pop && pop_process_io_handler) process_sp->PopProcessIOHandler(); return true; } bool Process::HijackProcessEvents(ListenerSP listener_sp) { if (listener_sp) { return HijackBroadcaster(listener_sp, eBroadcastBitStateChanged | eBroadcastBitInterrupt); } else return false; } void Process::RestoreProcessEvents() { RestoreBroadcaster(); } StateType Process::GetStateChangedEvents(EventSP &event_sp, const Timeout &timeout, ListenerSP hijack_listener_sp) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); LLDB_LOG(log, "timeout = {0}, event_sp)...", timeout); ListenerSP listener_sp = hijack_listener_sp; if (!listener_sp) listener_sp = m_listener_sp; StateType state = eStateInvalid; if (listener_sp->GetEventForBroadcasterWithType( this, eBroadcastBitStateChanged | eBroadcastBitInterrupt, event_sp, timeout)) { if (event_sp && event_sp->GetType() == eBroadcastBitStateChanged) state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); else LLDB_LOG(log, "got no event or was interrupted."); } LLDB_LOG(log, "timeout = {0}, event_sp) => {1}", timeout, state); return state; } Event *Process::PeekAtStateChangedEvents() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::%s...", __FUNCTION__); Event *event_ptr; event_ptr = m_listener_sp->PeekAtNextEventForBroadcasterWithType( this, eBroadcastBitStateChanged); if (log) { if (event_ptr) { log->Printf( "Process::%s (event_ptr) => %s", __FUNCTION__, StateAsCString(ProcessEventData::GetStateFromEvent(event_ptr))); } else { log->Printf("Process::%s no events found", __FUNCTION__); } } return event_ptr; } StateType Process::GetStateChangedEventsPrivate(EventSP &event_sp, const Timeout &timeout) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); LLDB_LOG(log, "timeout = {0}, event_sp)...", timeout); StateType state = eStateInvalid; if (m_private_state_listener_sp->GetEventForBroadcasterWithType( &m_private_state_broadcaster, eBroadcastBitStateChanged | eBroadcastBitInterrupt, event_sp, timeout)) if (event_sp && event_sp->GetType() == eBroadcastBitStateChanged) state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); LLDB_LOG(log, "timeout = {0}, event_sp) => {1}", timeout, state == eStateInvalid ? "TIMEOUT" : StateAsCString(state)); return state; } bool Process::GetEventsPrivate(EventSP &event_sp, const Timeout &timeout, bool control_only) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); LLDB_LOG(log, "timeout = {0}, event_sp)...", timeout); if (control_only) return m_private_state_listener_sp->GetEventForBroadcaster( &m_private_state_control_broadcaster, event_sp, timeout); else return m_private_state_listener_sp->GetEvent(event_sp, timeout); } bool Process::IsRunning() const { return StateIsRunningState(m_public_state.GetValue()); } int Process::GetExitStatus() { std::lock_guard guard(m_exit_status_mutex); if (m_public_state.GetValue() == eStateExited) return m_exit_status; return -1; } const char *Process::GetExitDescription() { std::lock_guard guard(m_exit_status_mutex); if (m_public_state.GetValue() == eStateExited && !m_exit_string.empty()) return m_exit_string.c_str(); return nullptr; } bool Process::SetExitStatus(int status, const char *cstr) { // Use a mutex to protect setting the exit status. std::lock_guard guard(m_exit_status_mutex); Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); if (log) log->Printf( "Process::SetExitStatus (status=%i (0x%8.8x), description=%s%s%s)", status, status, cstr ? "\"" : "", cstr ? cstr : "NULL", cstr ? "\"" : ""); // We were already in the exited state if (m_private_state.GetValue() == eStateExited) { if (log) log->Printf("Process::SetExitStatus () ignoring exit status because " "state was already set to eStateExited"); return false; } m_exit_status = status; if (cstr) m_exit_string = cstr; else m_exit_string.clear(); // Clear the last natural stop ID since it has a strong // reference to this process m_mod_id.SetStopEventForLastNaturalStopID(EventSP()); SetPrivateState(eStateExited); // Allow subclasses to do some cleanup DidExit(); return true; } bool Process::IsAlive() { switch (m_private_state.GetValue()) { case eStateConnected: case eStateAttaching: case eStateLaunching: case eStateStopped: case eStateRunning: case eStateStepping: case eStateCrashed: case eStateSuspended: return true; default: return false; } } // This static callback can be used to watch for local child processes on // the current host. The child process exits, the process will be // found in the global target list (we want to be completely sure that the // lldb_private::Process doesn't go away before we can deliver the signal. bool Process::SetProcessExitStatus( lldb::pid_t pid, bool exited, int signo, // Zero for no signal int exit_status // Exit value of process if signal is zero ) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::SetProcessExitStatus (pid=%" PRIu64 ", exited=%i, signal=%i, exit_status=%i)\n", pid, exited, signo, exit_status); if (exited) { TargetSP target_sp(Debugger::FindTargetWithProcessID(pid)); if (target_sp) { ProcessSP process_sp(target_sp->GetProcessSP()); if (process_sp) { const char *signal_cstr = nullptr; if (signo) signal_cstr = process_sp->GetUnixSignals()->GetSignalAsCString(signo); process_sp->SetExitStatus(exit_status, signal_cstr); } } return true; } return false; } void Process::UpdateThreadListIfNeeded() { const uint32_t stop_id = GetStopID(); if (m_thread_list.GetSize(false) == 0 || stop_id != m_thread_list.GetStopID()) { const StateType state = GetPrivateState(); if (StateIsStoppedState(state, true)) { std::lock_guard guard(m_thread_list.GetMutex()); // m_thread_list does have its own mutex, but we need to // hold onto the mutex between the call to UpdateThreadList(...) // and the os->UpdateThreadList(...) so it doesn't change on us ThreadList &old_thread_list = m_thread_list; ThreadList real_thread_list(this); ThreadList new_thread_list(this); // Always update the thread list with the protocol specific // thread list, but only update if "true" is returned if (UpdateThreadList(m_thread_list_real, real_thread_list)) { // Don't call into the OperatingSystem to update the thread list if we // are shutting down, since // that may call back into the SBAPI's, requiring the API lock which is // already held by whoever is // shutting us down, causing a deadlock. OperatingSystem *os = GetOperatingSystem(); if (os && !m_destroy_in_process) { // Clear any old backing threads where memory threads might have been // backed by actual threads from the lldb_private::Process subclass size_t num_old_threads = old_thread_list.GetSize(false); for (size_t i = 0; i < num_old_threads; ++i) old_thread_list.GetThreadAtIndex(i, false)->ClearBackingThread(); // Turn off dynamic types to ensure we don't run any expressions. // Objective C // can run an expression to determine if a SBValue is a dynamic type // or not // and we need to avoid this. OperatingSystem plug-ins can't run // expressions // that require running code... Target &target = GetTarget(); const lldb::DynamicValueType saved_prefer_dynamic = target.GetPreferDynamicValue(); if (saved_prefer_dynamic != lldb::eNoDynamicValues) target.SetPreferDynamicValue(lldb::eNoDynamicValues); // Now let the OperatingSystem plug-in update the thread list os->UpdateThreadList( old_thread_list, // Old list full of threads created by OS plug-in real_thread_list, // The actual thread list full of threads // created by each lldb_private::Process // subclass new_thread_list); // The new thread list that we will show to the // user that gets filled in if (saved_prefer_dynamic != lldb::eNoDynamicValues) target.SetPreferDynamicValue(saved_prefer_dynamic); } else { // No OS plug-in, the new thread list is the same as the real thread // list new_thread_list = real_thread_list; } m_thread_list_real.Update(real_thread_list); m_thread_list.Update(new_thread_list); m_thread_list.SetStopID(stop_id); if (GetLastNaturalStopID() != m_extended_thread_stop_id) { // Clear any extended threads that we may have accumulated previously m_extended_thread_list.Clear(); m_extended_thread_stop_id = GetLastNaturalStopID(); m_queue_list.Clear(); m_queue_list_stop_id = GetLastNaturalStopID(); } } } } } void Process::UpdateQueueListIfNeeded() { if (m_system_runtime_ap) { if (m_queue_list.GetSize() == 0 || m_queue_list_stop_id != GetLastNaturalStopID()) { const StateType state = GetPrivateState(); if (StateIsStoppedState(state, true)) { m_system_runtime_ap->PopulateQueueList(m_queue_list); m_queue_list_stop_id = GetLastNaturalStopID(); } } } } ThreadSP Process::CreateOSPluginThread(lldb::tid_t tid, lldb::addr_t context) { OperatingSystem *os = GetOperatingSystem(); if (os) return os->CreateThread(tid, context); return ThreadSP(); } uint32_t Process::GetNextThreadIndexID(uint64_t thread_id) { return AssignIndexIDToThread(thread_id); } bool Process::HasAssignedIndexIDToThread(uint64_t thread_id) { return (m_thread_id_to_index_id_map.find(thread_id) != m_thread_id_to_index_id_map.end()); } uint32_t Process::AssignIndexIDToThread(uint64_t thread_id) { uint32_t result = 0; std::map::iterator iterator = m_thread_id_to_index_id_map.find(thread_id); if (iterator == m_thread_id_to_index_id_map.end()) { result = ++m_thread_index_id; m_thread_id_to_index_id_map[thread_id] = result; } else { result = iterator->second; } return result; } StateType Process::GetState() { // If any other threads access this we will need a mutex for it return m_public_state.GetValue(); } bool Process::StateChangedIsExternallyHijacked() { if (IsHijackedForEvent(eBroadcastBitStateChanged)) { const char *hijacking_name = GetHijackingListenerName(); if (hijacking_name && strcmp(hijacking_name, "lldb.Process.ResumeSynchronous.hijack")) return true; } return false; } void Process::SetPublicState(StateType new_state, bool restarted) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::SetPublicState (state = %s, restarted = %i)", StateAsCString(new_state), restarted); const StateType old_state = m_public_state.GetValue(); m_public_state.SetValue(new_state); // On the transition from Run to Stopped, we unlock the writer end of the // run lock. The lock gets locked in Resume, which is the public API // to tell the program to run. if (!StateChangedIsExternallyHijacked()) { if (new_state == eStateDetached) { if (log) log->Printf( "Process::SetPublicState (%s) -- unlocking run lock for detach", StateAsCString(new_state)); m_public_run_lock.SetStopped(); } else { const bool old_state_is_stopped = StateIsStoppedState(old_state, false); const bool new_state_is_stopped = StateIsStoppedState(new_state, false); if ((old_state_is_stopped != new_state_is_stopped)) { if (new_state_is_stopped && !restarted) { if (log) log->Printf("Process::SetPublicState (%s) -- unlocking run lock", StateAsCString(new_state)); m_public_run_lock.SetStopped(); } } } } } Status Process::Resume() { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::Resume -- locking run lock"); if (!m_public_run_lock.TrySetRunning()) { Status error("Resume request failed - process still running."); if (log) log->Printf("Process::Resume: -- TrySetRunning failed, not resuming."); return error; } return PrivateResume(); } Status Process::ResumeSynchronous(Stream *stream) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::ResumeSynchronous -- locking run lock"); if (!m_public_run_lock.TrySetRunning()) { Status error("Resume request failed - process still running."); if (log) log->Printf("Process::Resume: -- TrySetRunning failed, not resuming."); return error; } ListenerSP listener_sp( Listener::MakeListener("lldb.Process.ResumeSynchronous.hijack")); HijackProcessEvents(listener_sp); Status error = PrivateResume(); if (error.Success()) { StateType state = WaitForProcessToStop(llvm::None, NULL, true, listener_sp, stream); const bool must_be_alive = false; // eStateExited is ok, so this must be false if (!StateIsStoppedState(state, must_be_alive)) error.SetErrorStringWithFormat( "process not in stopped state after synchronous resume: %s", StateAsCString(state)); } // Undo the hijacking of process events... RestoreProcessEvents(); return error; } StateType Process::GetPrivateState() { return m_private_state.GetValue(); } void Process::SetPrivateState(StateType new_state) { if (m_finalize_called) return; Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); bool state_changed = false; if (log) log->Printf("Process::SetPrivateState (%s)", StateAsCString(new_state)); std::lock_guard thread_guard(m_thread_list.GetMutex()); std::lock_guard guard(m_private_state.GetMutex()); const StateType old_state = m_private_state.GetValueNoLock(); state_changed = old_state != new_state; const bool old_state_is_stopped = StateIsStoppedState(old_state, false); const bool new_state_is_stopped = StateIsStoppedState(new_state, false); if (old_state_is_stopped != new_state_is_stopped) { if (new_state_is_stopped) m_private_run_lock.SetStopped(); else m_private_run_lock.SetRunning(); } if (state_changed) { m_private_state.SetValueNoLock(new_state); EventSP event_sp( new Event(eBroadcastBitStateChanged, new ProcessEventData(shared_from_this(), new_state))); if (StateIsStoppedState(new_state, false)) { // Note, this currently assumes that all threads in the list // stop when the process stops. In the future we will want to // support a debugging model where some threads continue to run // while others are stopped. When that happens we will either need // a way for the thread list to identify which threads are stopping // or create a special thread list containing only threads which // actually stopped. // // The process plugin is responsible for managing the actual // behavior of the threads and should have stopped any threads // that are going to stop before we get here. m_thread_list.DidStop(); m_mod_id.BumpStopID(); if (!m_mod_id.IsLastResumeForUserExpression()) m_mod_id.SetStopEventForLastNaturalStopID(event_sp); m_memory_cache.Clear(); if (log) log->Printf("Process::SetPrivateState (%s) stop_id = %u", StateAsCString(new_state), m_mod_id.GetStopID()); } // Use our target to get a shared pointer to ourselves... if (m_finalize_called && !PrivateStateThreadIsValid()) BroadcastEvent(event_sp); else m_private_state_broadcaster.BroadcastEvent(event_sp); } else { if (log) log->Printf( "Process::SetPrivateState (%s) state didn't change. Ignoring...", StateAsCString(new_state)); } } void Process::SetRunningUserExpression(bool on) { m_mod_id.SetRunningUserExpression(on); } addr_t Process::GetImageInfoAddress() { return LLDB_INVALID_ADDRESS; } const lldb::ABISP &Process::GetABI() { if (!m_abi_sp) m_abi_sp = ABI::FindPlugin(GetTarget().GetArchitecture()); return m_abi_sp; } LanguageRuntime *Process::GetLanguageRuntime(lldb::LanguageType language, bool retry_if_null) { if (m_finalizing) return nullptr; LanguageRuntimeCollection::iterator pos; pos = m_language_runtimes.find(language); if (pos == m_language_runtimes.end() || (retry_if_null && !(*pos).second)) { lldb::LanguageRuntimeSP runtime_sp( LanguageRuntime::FindPlugin(this, language)); m_language_runtimes[language] = runtime_sp; return runtime_sp.get(); } else return (*pos).second.get(); } CPPLanguageRuntime *Process::GetCPPLanguageRuntime(bool retry_if_null) { LanguageRuntime *runtime = GetLanguageRuntime(eLanguageTypeC_plus_plus, retry_if_null); if (runtime != nullptr && runtime->GetLanguageType() == eLanguageTypeC_plus_plus) return static_cast(runtime); return nullptr; } ObjCLanguageRuntime *Process::GetObjCLanguageRuntime(bool retry_if_null) { LanguageRuntime *runtime = GetLanguageRuntime(eLanguageTypeObjC, retry_if_null); if (runtime != nullptr && runtime->GetLanguageType() == eLanguageTypeObjC) return static_cast(runtime); return nullptr; } bool Process::IsPossibleDynamicValue(ValueObject &in_value) { if (m_finalizing) return false; if (in_value.IsDynamic()) return false; LanguageType known_type = in_value.GetObjectRuntimeLanguage(); if (known_type != eLanguageTypeUnknown && known_type != eLanguageTypeC) { LanguageRuntime *runtime = GetLanguageRuntime(known_type); return runtime ? runtime->CouldHaveDynamicValue(in_value) : false; } LanguageRuntime *cpp_runtime = GetLanguageRuntime(eLanguageTypeC_plus_plus); if (cpp_runtime && cpp_runtime->CouldHaveDynamicValue(in_value)) return true; LanguageRuntime *objc_runtime = GetLanguageRuntime(eLanguageTypeObjC); return objc_runtime ? objc_runtime->CouldHaveDynamicValue(in_value) : false; } void Process::SetDynamicCheckers(DynamicCheckerFunctions *dynamic_checkers) { m_dynamic_checkers_ap.reset(dynamic_checkers); } BreakpointSiteList &Process::GetBreakpointSiteList() { return m_breakpoint_site_list; } const BreakpointSiteList &Process::GetBreakpointSiteList() const { return m_breakpoint_site_list; } void Process::DisableAllBreakpointSites() { m_breakpoint_site_list.ForEach([this](BreakpointSite *bp_site) -> void { // bp_site->SetEnabled(true); DisableBreakpointSite(bp_site); }); } Status Process::ClearBreakpointSiteByID(lldb::user_id_t break_id) { Status error(DisableBreakpointSiteByID(break_id)); if (error.Success()) m_breakpoint_site_list.Remove(break_id); return error; } Status Process::DisableBreakpointSiteByID(lldb::user_id_t break_id) { Status error; BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID(break_id); if (bp_site_sp) { if (bp_site_sp->IsEnabled()) error = DisableBreakpointSite(bp_site_sp.get()); } else { error.SetErrorStringWithFormat("invalid breakpoint site ID: %" PRIu64, break_id); } return error; } Status Process::EnableBreakpointSiteByID(lldb::user_id_t break_id) { Status error; BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID(break_id); if (bp_site_sp) { if (!bp_site_sp->IsEnabled()) error = EnableBreakpointSite(bp_site_sp.get()); } else { error.SetErrorStringWithFormat("invalid breakpoint site ID: %" PRIu64, break_id); } return error; } lldb::break_id_t Process::CreateBreakpointSite(const BreakpointLocationSP &owner, bool use_hardware) { addr_t load_addr = LLDB_INVALID_ADDRESS; bool show_error = true; switch (GetState()) { case eStateInvalid: case eStateUnloaded: case eStateConnected: case eStateAttaching: case eStateLaunching: case eStateDetached: case eStateExited: show_error = false; break; case eStateStopped: case eStateRunning: case eStateStepping: case eStateCrashed: case eStateSuspended: show_error = IsAlive(); break; } // Reset the IsIndirect flag here, in case the location changes from // pointing to a indirect symbol to a regular symbol. owner->SetIsIndirect(false); if (owner->ShouldResolveIndirectFunctions()) { Symbol *symbol = owner->GetAddress().CalculateSymbolContextSymbol(); if (symbol && symbol->IsIndirect()) { Status error; Address symbol_address = symbol->GetAddress(); load_addr = ResolveIndirectFunction(&symbol_address, error); if (!error.Success() && show_error) { GetTarget().GetDebugger().GetErrorFile()->Printf( "warning: failed to resolve indirect function at 0x%" PRIx64 " for breakpoint %i.%i: %s\n", symbol->GetLoadAddress(&GetTarget()), owner->GetBreakpoint().GetID(), owner->GetID(), error.AsCString() ? error.AsCString() : "unknown error"); return LLDB_INVALID_BREAK_ID; } Address resolved_address(load_addr); load_addr = resolved_address.GetOpcodeLoadAddress(&GetTarget()); owner->SetIsIndirect(true); } else load_addr = owner->GetAddress().GetOpcodeLoadAddress(&GetTarget()); } else load_addr = owner->GetAddress().GetOpcodeLoadAddress(&GetTarget()); if (load_addr != LLDB_INVALID_ADDRESS) { BreakpointSiteSP bp_site_sp; // Look up this breakpoint site. If it exists, then add this new owner, // otherwise // create a new breakpoint site and add it. bp_site_sp = m_breakpoint_site_list.FindByAddress(load_addr); if (bp_site_sp) { bp_site_sp->AddOwner(owner); owner->SetBreakpointSite(bp_site_sp); return bp_site_sp->GetID(); } else { bp_site_sp.reset(new BreakpointSite(&m_breakpoint_site_list, owner, load_addr, use_hardware)); if (bp_site_sp) { Status error = EnableBreakpointSite(bp_site_sp.get()); if (error.Success()) { owner->SetBreakpointSite(bp_site_sp); return m_breakpoint_site_list.Add(bp_site_sp); } else { if (show_error) { // Report error for setting breakpoint... GetTarget().GetDebugger().GetErrorFile()->Printf( "warning: failed to set breakpoint site at 0x%" PRIx64 " for breakpoint %i.%i: %s\n", load_addr, owner->GetBreakpoint().GetID(), owner->GetID(), error.AsCString() ? error.AsCString() : "unknown error"); } } } } } // We failed to enable the breakpoint return LLDB_INVALID_BREAK_ID; } void Process::RemoveOwnerFromBreakpointSite(lldb::user_id_t owner_id, lldb::user_id_t owner_loc_id, BreakpointSiteSP &bp_site_sp) { uint32_t num_owners = bp_site_sp->RemoveOwner(owner_id, owner_loc_id); if (num_owners == 0) { // Don't try to disable the site if we don't have a live process anymore. if (IsAlive()) DisableBreakpointSite(bp_site_sp.get()); m_breakpoint_site_list.RemoveByAddress(bp_site_sp->GetLoadAddress()); } } size_t Process::RemoveBreakpointOpcodesFromBuffer(addr_t bp_addr, size_t size, uint8_t *buf) const { size_t bytes_removed = 0; BreakpointSiteList bp_sites_in_range; if (m_breakpoint_site_list.FindInRange(bp_addr, bp_addr + size, bp_sites_in_range)) { bp_sites_in_range.ForEach([bp_addr, size, buf](BreakpointSite *bp_site) -> void { if (bp_site->GetType() == BreakpointSite::eSoftware) { addr_t intersect_addr; size_t intersect_size; size_t opcode_offset; if (bp_site->IntersectsRange(bp_addr, size, &intersect_addr, &intersect_size, &opcode_offset)) { assert(bp_addr <= intersect_addr && intersect_addr < bp_addr + size); assert(bp_addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= bp_addr + size); assert(opcode_offset + intersect_size <= bp_site->GetByteSize()); size_t buf_offset = intersect_addr - bp_addr; ::memcpy(buf + buf_offset, bp_site->GetSavedOpcodeBytes() + opcode_offset, intersect_size); } } }); } return bytes_removed; } size_t Process::GetSoftwareBreakpointTrapOpcode(BreakpointSite *bp_site) { PlatformSP platform_sp(GetTarget().GetPlatform()); if (platform_sp) return platform_sp->GetSoftwareBreakpointTrapOpcode(GetTarget(), bp_site); return 0; } Status Process::EnableSoftwareBreakpoint(BreakpointSite *bp_site) { Status error; assert(bp_site != nullptr); Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); const addr_t bp_addr = bp_site->GetLoadAddress(); if (log) log->Printf( "Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64, bp_site->GetID(), (uint64_t)bp_addr); if (bp_site->IsEnabled()) { if (log) log->Printf( "Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 " -- already enabled", bp_site->GetID(), (uint64_t)bp_addr); return error; } if (bp_addr == LLDB_INVALID_ADDRESS) { error.SetErrorString("BreakpointSite contains an invalid load address."); return error; } // Ask the lldb::Process subclass to fill in the correct software breakpoint // trap for the breakpoint site const size_t bp_opcode_size = GetSoftwareBreakpointTrapOpcode(bp_site); if (bp_opcode_size == 0) { error.SetErrorStringWithFormat("Process::GetSoftwareBreakpointTrapOpcode() " "returned zero, unable to get breakpoint " "trap for address 0x%" PRIx64, bp_addr); } else { const uint8_t *const bp_opcode_bytes = bp_site->GetTrapOpcodeBytes(); if (bp_opcode_bytes == nullptr) { error.SetErrorString( "BreakpointSite doesn't contain a valid breakpoint trap opcode."); return error; } // Save the original opcode by reading it if (DoReadMemory(bp_addr, bp_site->GetSavedOpcodeBytes(), bp_opcode_size, error) == bp_opcode_size) { // Write a software breakpoint in place of the original opcode if (DoWriteMemory(bp_addr, bp_opcode_bytes, bp_opcode_size, error) == bp_opcode_size) { uint8_t verify_bp_opcode_bytes[64]; if (DoReadMemory(bp_addr, verify_bp_opcode_bytes, bp_opcode_size, error) == bp_opcode_size) { if (::memcmp(bp_opcode_bytes, verify_bp_opcode_bytes, bp_opcode_size) == 0) { bp_site->SetEnabled(true); bp_site->SetType(BreakpointSite::eSoftware); if (log) log->Printf("Process::EnableSoftwareBreakpoint (site_id = %d) " "addr = 0x%" PRIx64 " -- SUCCESS", bp_site->GetID(), (uint64_t)bp_addr); } else error.SetErrorString( "failed to verify the breakpoint trap in memory."); } else error.SetErrorString( "Unable to read memory to verify breakpoint trap."); } else error.SetErrorString("Unable to write breakpoint trap to memory."); } else error.SetErrorString("Unable to read memory at breakpoint address."); } if (log && error.Fail()) log->Printf( "Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 " -- FAILED: %s", bp_site->GetID(), (uint64_t)bp_addr, error.AsCString()); return error; } Status Process::DisableSoftwareBreakpoint(BreakpointSite *bp_site) { Status error; assert(bp_site != nullptr); Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); addr_t bp_addr = bp_site->GetLoadAddress(); lldb::user_id_t breakID = bp_site->GetID(); if (log) log->Printf("Process::DisableSoftwareBreakpoint (breakID = %" PRIu64 ") addr = 0x%" PRIx64, breakID, (uint64_t)bp_addr); if (bp_site->IsHardware()) { error.SetErrorString("Breakpoint site is a hardware breakpoint."); } else if (bp_site->IsEnabled()) { const size_t break_op_size = bp_site->GetByteSize(); const uint8_t *const break_op = bp_site->GetTrapOpcodeBytes(); if (break_op_size > 0) { // Clear a software breakpoint instruction uint8_t curr_break_op[8]; assert(break_op_size <= sizeof(curr_break_op)); bool break_op_found = false; // Read the breakpoint opcode if (DoReadMemory(bp_addr, curr_break_op, break_op_size, error) == break_op_size) { bool verify = false; // Make sure the breakpoint opcode exists at this address if (::memcmp(curr_break_op, break_op, break_op_size) == 0) { break_op_found = true; // We found a valid breakpoint opcode at this address, now restore // the saved opcode. if (DoWriteMemory(bp_addr, bp_site->GetSavedOpcodeBytes(), break_op_size, error) == break_op_size) { verify = true; } else error.SetErrorString( "Memory write failed when restoring original opcode."); } else { error.SetErrorString( "Original breakpoint trap is no longer in memory."); // Set verify to true and so we can check if the original opcode has // already been restored verify = true; } if (verify) { uint8_t verify_opcode[8]; assert(break_op_size < sizeof(verify_opcode)); // Verify that our original opcode made it back to the inferior if (DoReadMemory(bp_addr, verify_opcode, break_op_size, error) == break_op_size) { // compare the memory we just read with the original opcode if (::memcmp(bp_site->GetSavedOpcodeBytes(), verify_opcode, break_op_size) == 0) { // SUCCESS bp_site->SetEnabled(false); if (log) log->Printf("Process::DisableSoftwareBreakpoint (site_id = %d) " "addr = 0x%" PRIx64 " -- SUCCESS", bp_site->GetID(), (uint64_t)bp_addr); return error; } else { if (break_op_found) error.SetErrorString("Failed to restore original opcode."); } } else error.SetErrorString("Failed to read memory to verify that " "breakpoint trap was restored."); } } else error.SetErrorString( "Unable to read memory that should contain the breakpoint trap."); } } else { if (log) log->Printf( "Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 " -- already disabled", bp_site->GetID(), (uint64_t)bp_addr); return error; } if (log) log->Printf( "Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 " -- FAILED: %s", bp_site->GetID(), (uint64_t)bp_addr, error.AsCString()); return error; } // Uncomment to verify memory caching works after making changes to caching code //#define VERIFY_MEMORY_READS size_t Process::ReadMemory(addr_t addr, void *buf, size_t size, Status &error) { error.Clear(); if (!GetDisableMemoryCache()) { #if defined(VERIFY_MEMORY_READS) // Memory caching is enabled, with debug verification if (buf && size) { // Uncomment the line below to make sure memory caching is working. // I ran this through the test suite and got no assertions, so I am // pretty confident this is working well. If any changes are made to // memory caching, uncomment the line below and test your changes! // Verify all memory reads by using the cache first, then redundantly // reading the same memory from the inferior and comparing to make sure // everything is exactly the same. std::string verify_buf(size, '\0'); assert(verify_buf.size() == size); const size_t cache_bytes_read = m_memory_cache.Read(this, addr, buf, size, error); Status verify_error; const size_t verify_bytes_read = ReadMemoryFromInferior(addr, const_cast(verify_buf.data()), verify_buf.size(), verify_error); assert(cache_bytes_read == verify_bytes_read); assert(memcmp(buf, verify_buf.data(), verify_buf.size()) == 0); assert(verify_error.Success() == error.Success()); return cache_bytes_read; } return 0; #else // !defined(VERIFY_MEMORY_READS) // Memory caching is enabled, without debug verification return m_memory_cache.Read(addr, buf, size, error); #endif // defined (VERIFY_MEMORY_READS) } else { // Memory caching is disabled return ReadMemoryFromInferior(addr, buf, size, error); } } size_t Process::ReadCStringFromMemory(addr_t addr, std::string &out_str, Status &error) { char buf[256]; out_str.clear(); addr_t curr_addr = addr; while (true) { size_t length = ReadCStringFromMemory(curr_addr, buf, sizeof(buf), error); if (length == 0) break; out_str.append(buf, length); // If we got "length - 1" bytes, we didn't get the whole C string, we // need to read some more characters if (length == sizeof(buf) - 1) curr_addr += length; else break; } return out_str.size(); } size_t Process::ReadStringFromMemory(addr_t addr, char *dst, size_t max_bytes, Status &error, size_t type_width) { size_t total_bytes_read = 0; if (dst && max_bytes && type_width && max_bytes >= type_width) { // Ensure a null terminator independent of the number of bytes that is read. memset(dst, 0, max_bytes); size_t bytes_left = max_bytes - type_width; const char terminator[4] = {'\0', '\0', '\0', '\0'}; assert(sizeof(terminator) >= type_width && "Attempting to validate a " "string with more than 4 bytes " "per character!"); addr_t curr_addr = addr; const size_t cache_line_size = m_memory_cache.GetMemoryCacheLineSize(); char *curr_dst = dst; error.Clear(); while (bytes_left > 0 && error.Success()) { addr_t cache_line_bytes_left = cache_line_size - (curr_addr % cache_line_size); addr_t bytes_to_read = std::min(bytes_left, cache_line_bytes_left); size_t bytes_read = ReadMemory(curr_addr, curr_dst, bytes_to_read, error); if (bytes_read == 0) break; // Search for a null terminator of correct size and alignment in // bytes_read size_t aligned_start = total_bytes_read - total_bytes_read % type_width; for (size_t i = aligned_start; i + type_width <= total_bytes_read + bytes_read; i += type_width) if (::memcmp(&dst[i], terminator, type_width) == 0) { error.Clear(); return i; } total_bytes_read += bytes_read; curr_dst += bytes_read; curr_addr += bytes_read; bytes_left -= bytes_read; } } else { if (max_bytes) error.SetErrorString("invalid arguments"); } return total_bytes_read; } // Deprecated in favor of ReadStringFromMemory which has wchar support and // correct code to find // null terminators. size_t Process::ReadCStringFromMemory(addr_t addr, char *dst, size_t dst_max_len, Status &result_error) { size_t total_cstr_len = 0; if (dst && dst_max_len) { result_error.Clear(); // NULL out everything just to be safe memset(dst, 0, dst_max_len); Status error; addr_t curr_addr = addr; const size_t cache_line_size = m_memory_cache.GetMemoryCacheLineSize(); size_t bytes_left = dst_max_len - 1; char *curr_dst = dst; while (bytes_left > 0) { addr_t cache_line_bytes_left = cache_line_size - (curr_addr % cache_line_size); addr_t bytes_to_read = std::min(bytes_left, cache_line_bytes_left); size_t bytes_read = ReadMemory(curr_addr, curr_dst, bytes_to_read, error); if (bytes_read == 0) { result_error = error; dst[total_cstr_len] = '\0'; break; } const size_t len = strlen(curr_dst); total_cstr_len += len; if (len < bytes_to_read) break; curr_dst += bytes_read; curr_addr += bytes_read; bytes_left -= bytes_read; } } else { if (dst == nullptr) result_error.SetErrorString("invalid arguments"); else result_error.Clear(); } return total_cstr_len; } size_t Process::ReadMemoryFromInferior(addr_t addr, void *buf, size_t size, Status &error) { if (buf == nullptr || size == 0) return 0; size_t bytes_read = 0; uint8_t *bytes = (uint8_t *)buf; while (bytes_read < size) { const size_t curr_size = size - bytes_read; const size_t curr_bytes_read = DoReadMemory(addr + bytes_read, bytes + bytes_read, curr_size, error); bytes_read += curr_bytes_read; if (curr_bytes_read == curr_size || curr_bytes_read == 0) break; } // Replace any software breakpoint opcodes that fall into this range back // into "buf" before we return if (bytes_read > 0) RemoveBreakpointOpcodesFromBuffer(addr, bytes_read, (uint8_t *)buf); return bytes_read; } uint64_t Process::ReadUnsignedIntegerFromMemory(lldb::addr_t vm_addr, size_t integer_byte_size, uint64_t fail_value, Status &error) { Scalar scalar; if (ReadScalarIntegerFromMemory(vm_addr, integer_byte_size, false, scalar, error)) return scalar.ULongLong(fail_value); return fail_value; } int64_t Process::ReadSignedIntegerFromMemory(lldb::addr_t vm_addr, size_t integer_byte_size, int64_t fail_value, Status &error) { Scalar scalar; if (ReadScalarIntegerFromMemory(vm_addr, integer_byte_size, true, scalar, error)) return scalar.SLongLong(fail_value); return fail_value; } addr_t Process::ReadPointerFromMemory(lldb::addr_t vm_addr, Status &error) { Scalar scalar; if (ReadScalarIntegerFromMemory(vm_addr, GetAddressByteSize(), false, scalar, error)) return scalar.ULongLong(LLDB_INVALID_ADDRESS); return LLDB_INVALID_ADDRESS; } bool Process::WritePointerToMemory(lldb::addr_t vm_addr, lldb::addr_t ptr_value, Status &error) { Scalar scalar; const uint32_t addr_byte_size = GetAddressByteSize(); if (addr_byte_size <= 4) scalar = (uint32_t)ptr_value; else scalar = ptr_value; return WriteScalarToMemory(vm_addr, scalar, addr_byte_size, error) == addr_byte_size; } size_t Process::WriteMemoryPrivate(addr_t addr, const void *buf, size_t size, Status &error) { size_t bytes_written = 0; const uint8_t *bytes = (const uint8_t *)buf; while (bytes_written < size) { const size_t curr_size = size - bytes_written; const size_t curr_bytes_written = DoWriteMemory( addr + bytes_written, bytes + bytes_written, curr_size, error); bytes_written += curr_bytes_written; if (curr_bytes_written == curr_size || curr_bytes_written == 0) break; } return bytes_written; } size_t Process::WriteMemory(addr_t addr, const void *buf, size_t size, Status &error) { #if defined(ENABLE_MEMORY_CACHING) m_memory_cache.Flush(addr, size); #endif if (buf == nullptr || size == 0) return 0; m_mod_id.BumpMemoryID(); // We need to write any data that would go where any current software traps // (enabled software breakpoints) any software traps (breakpoints) that we // may have placed in our tasks memory. BreakpointSiteList bp_sites_in_range; if (m_breakpoint_site_list.FindInRange(addr, addr + size, bp_sites_in_range)) { // No breakpoint sites overlap if (bp_sites_in_range.IsEmpty()) return WriteMemoryPrivate(addr, buf, size, error); else { const uint8_t *ubuf = (const uint8_t *)buf; uint64_t bytes_written = 0; bp_sites_in_range.ForEach([this, addr, size, &bytes_written, &ubuf, &error](BreakpointSite *bp) -> void { if (error.Success()) { addr_t intersect_addr; size_t intersect_size; size_t opcode_offset; const bool intersects = bp->IntersectsRange( addr, size, &intersect_addr, &intersect_size, &opcode_offset); UNUSED_IF_ASSERT_DISABLED(intersects); assert(intersects); assert(addr <= intersect_addr && intersect_addr < addr + size); assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size); assert(opcode_offset + intersect_size <= bp->GetByteSize()); // Check for bytes before this breakpoint const addr_t curr_addr = addr + bytes_written; if (intersect_addr > curr_addr) { // There are some bytes before this breakpoint that we need to // just write to memory size_t curr_size = intersect_addr - curr_addr; size_t curr_bytes_written = WriteMemoryPrivate( curr_addr, ubuf + bytes_written, curr_size, error); bytes_written += curr_bytes_written; if (curr_bytes_written != curr_size) { // We weren't able to write all of the requested bytes, we // are done looping and will return the number of bytes that // we have written so far. if (error.Success()) error.SetErrorToGenericError(); } } // Now write any bytes that would cover up any software breakpoints // directly into the breakpoint opcode buffer ::memcpy(bp->GetSavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, intersect_size); bytes_written += intersect_size; } }); if (bytes_written < size) WriteMemoryPrivate(addr + bytes_written, ubuf + bytes_written, size - bytes_written, error); } } else { return WriteMemoryPrivate(addr, buf, size, error); } // Write any remaining bytes after the last breakpoint if we have any left return 0; // bytes_written; } size_t Process::WriteScalarToMemory(addr_t addr, const Scalar &scalar, size_t byte_size, Status &error) { if (byte_size == UINT32_MAX) byte_size = scalar.GetByteSize(); if (byte_size > 0) { uint8_t buf[32]; const size_t mem_size = scalar.GetAsMemoryData(buf, byte_size, GetByteOrder(), error); if (mem_size > 0) return WriteMemory(addr, buf, mem_size, error); else error.SetErrorString("failed to get scalar as memory data"); } else { error.SetErrorString("invalid scalar value"); } return 0; } size_t Process::ReadScalarIntegerFromMemory(addr_t addr, uint32_t byte_size, bool is_signed, Scalar &scalar, Status &error) { uint64_t uval = 0; if (byte_size == 0) { error.SetErrorString("byte size is zero"); } else if (byte_size & (byte_size - 1)) { error.SetErrorStringWithFormat("byte size %u is not a power of 2", byte_size); } else if (byte_size <= sizeof(uval)) { const size_t bytes_read = ReadMemory(addr, &uval, byte_size, error); if (bytes_read == byte_size) { DataExtractor data(&uval, sizeof(uval), GetByteOrder(), GetAddressByteSize()); lldb::offset_t offset = 0; if (byte_size <= 4) scalar = data.GetMaxU32(&offset, byte_size); else scalar = data.GetMaxU64(&offset, byte_size); if (is_signed) scalar.SignExtend(byte_size * 8); return bytes_read; } } else { error.SetErrorStringWithFormat( "byte size of %u is too large for integer scalar type", byte_size); } return 0; } #define USE_ALLOCATE_MEMORY_CACHE 1 addr_t Process::AllocateMemory(size_t size, uint32_t permissions, Status &error) { if (GetPrivateState() != eStateStopped) return LLDB_INVALID_ADDRESS; #if defined(USE_ALLOCATE_MEMORY_CACHE) return m_allocated_memory_cache.AllocateMemory(size, permissions, error); #else addr_t allocated_addr = DoAllocateMemory(size, permissions, error); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::AllocateMemory(size=%" PRIu64 ", permissions=%s) => 0x%16.16" PRIx64 " (m_stop_id = %u m_memory_id = %u)", (uint64_t)size, GetPermissionsAsCString(permissions), (uint64_t)allocated_addr, m_mod_id.GetStopID(), m_mod_id.GetMemoryID()); return allocated_addr; #endif } addr_t Process::CallocateMemory(size_t size, uint32_t permissions, Status &error) { addr_t return_addr = AllocateMemory(size, permissions, error); if (error.Success()) { std::string buffer(size, 0); WriteMemory(return_addr, buffer.c_str(), size, error); } return return_addr; } bool Process::CanJIT() { if (m_can_jit == eCanJITDontKnow) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); Status err; uint64_t allocated_memory = AllocateMemory( 8, ePermissionsReadable | ePermissionsWritable | ePermissionsExecutable, err); if (err.Success()) { m_can_jit = eCanJITYes; if (log) log->Printf("Process::%s pid %" PRIu64 " allocation test passed, CanJIT () is true", __FUNCTION__, GetID()); } else { m_can_jit = eCanJITNo; if (log) log->Printf("Process::%s pid %" PRIu64 " allocation test failed, CanJIT () is false: %s", __FUNCTION__, GetID(), err.AsCString()); } DeallocateMemory(allocated_memory); } return m_can_jit == eCanJITYes; } void Process::SetCanJIT(bool can_jit) { m_can_jit = (can_jit ? eCanJITYes : eCanJITNo); } void Process::SetCanRunCode(bool can_run_code) { SetCanJIT(can_run_code); m_can_interpret_function_calls = can_run_code; } Status Process::DeallocateMemory(addr_t ptr) { Status error; #if defined(USE_ALLOCATE_MEMORY_CACHE) if (!m_allocated_memory_cache.DeallocateMemory(ptr)) { error.SetErrorStringWithFormat( "deallocation of memory at 0x%" PRIx64 " failed.", (uint64_t)ptr); } #else error = DoDeallocateMemory(ptr); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::DeallocateMemory(addr=0x%16.16" PRIx64 ") => err = %s (m_stop_id = %u, m_memory_id = %u)", ptr, error.AsCString("SUCCESS"), m_mod_id.GetStopID(), m_mod_id.GetMemoryID()); #endif return error; } ModuleSP Process::ReadModuleFromMemory(const FileSpec &file_spec, lldb::addr_t header_addr, size_t size_to_read) { Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); if (log) { log->Printf("Process::ReadModuleFromMemory reading %s binary from memory", file_spec.GetPath().c_str()); } ModuleSP module_sp(new Module(file_spec, ArchSpec())); if (module_sp) { Status error; ObjectFile *objfile = module_sp->GetMemoryObjectFile( shared_from_this(), header_addr, error, size_to_read); if (objfile) return module_sp; } return ModuleSP(); } bool Process::GetLoadAddressPermissions(lldb::addr_t load_addr, uint32_t &permissions) { MemoryRegionInfo range_info; permissions = 0; Status error(GetMemoryRegionInfo(load_addr, range_info)); if (!error.Success()) return false; if (range_info.GetReadable() == MemoryRegionInfo::eDontKnow || range_info.GetWritable() == MemoryRegionInfo::eDontKnow || range_info.GetExecutable() == MemoryRegionInfo::eDontKnow) { return false; } if (range_info.GetReadable() == MemoryRegionInfo::eYes) permissions |= lldb::ePermissionsReadable; if (range_info.GetWritable() == MemoryRegionInfo::eYes) permissions |= lldb::ePermissionsWritable; if (range_info.GetExecutable() == MemoryRegionInfo::eYes) permissions |= lldb::ePermissionsExecutable; return true; } Status Process::EnableWatchpoint(Watchpoint *watchpoint, bool notify) { Status error; error.SetErrorString("watchpoints are not supported"); return error; } Status Process::DisableWatchpoint(Watchpoint *watchpoint, bool notify) { Status error; error.SetErrorString("watchpoints are not supported"); return error; } StateType Process::WaitForProcessStopPrivate(EventSP &event_sp, const Timeout &timeout) { StateType state; // Now wait for the process to launch and return control to us, and then // call DidLaunch: while (true) { event_sp.reset(); state = GetStateChangedEventsPrivate(event_sp, timeout); if (StateIsStoppedState(state, false)) break; // If state is invalid, then we timed out if (state == eStateInvalid) break; if (event_sp) HandlePrivateEvent(event_sp); } return state; } void Process::LoadOperatingSystemPlugin(bool flush) { if (flush) m_thread_list.Clear(); m_os_ap.reset(OperatingSystem::FindPlugin(this, nullptr)); if (flush) Flush(); } Status Process::Launch(ProcessLaunchInfo &launch_info) { Status error; m_abi_sp.reset(); m_dyld_ap.reset(); m_jit_loaders_ap.reset(); m_system_runtime_ap.reset(); m_os_ap.reset(); m_process_input_reader.reset(); m_stop_info_override_callback = nullptr; Module *exe_module = GetTarget().GetExecutableModulePointer(); if (exe_module) { char local_exec_file_path[PATH_MAX]; char platform_exec_file_path[PATH_MAX]; exe_module->GetFileSpec().GetPath(local_exec_file_path, sizeof(local_exec_file_path)); exe_module->GetPlatformFileSpec().GetPath(platform_exec_file_path, sizeof(platform_exec_file_path)); if (exe_module->GetFileSpec().Exists()) { // Install anything that might need to be installed prior to launching. // For host systems, this will do nothing, but if we are connected to a // remote platform it will install any needed binaries error = GetTarget().Install(&launch_info); if (error.Fail()) return error; if (PrivateStateThreadIsValid()) PausePrivateStateThread(); error = WillLaunch(exe_module); if (error.Success()) { const bool restarted = false; SetPublicState(eStateLaunching, restarted); m_should_detach = false; if (m_public_run_lock.TrySetRunning()) { // Now launch using these arguments. error = DoLaunch(exe_module, launch_info); } else { // This shouldn't happen error.SetErrorString("failed to acquire process run lock"); } if (error.Fail()) { if (GetID() != LLDB_INVALID_PROCESS_ID) { SetID(LLDB_INVALID_PROCESS_ID); const char *error_string = error.AsCString(); if (error_string == nullptr) error_string = "launch failed"; SetExitStatus(-1, error_string); } } else { EventSP event_sp; StateType state = WaitForProcessStopPrivate(event_sp, seconds(10)); if (state == eStateInvalid || !event_sp) { // We were able to launch the process, but we failed to // catch the initial stop. error.SetErrorString("failed to catch stop after launch"); SetExitStatus(0, "failed to catch stop after launch"); Destroy(false); } else if (state == eStateStopped || state == eStateCrashed) { DidLaunch(); DynamicLoader *dyld = GetDynamicLoader(); if (dyld) dyld->DidLaunch(); GetJITLoaders().DidLaunch(); SystemRuntime *system_runtime = GetSystemRuntime(); if (system_runtime) system_runtime->DidLaunch(); if (!m_os_ap) LoadOperatingSystemPlugin(false); // We successfully launched the process and stopped, // now it the right time to set up signal filters before resuming. UpdateAutomaticSignalFiltering(); // Note, the stop event was consumed above, but not handled. This // was done // to give DidLaunch a chance to run. The target is either stopped // or crashed. // Directly set the state. This is done to prevent a stop message // with a bunch // of spurious output on thread status, as well as not pop a // ProcessIOHandler. SetPublicState(state, false); if (PrivateStateThreadIsValid()) ResumePrivateStateThread(); else StartPrivateStateThread(); m_stop_info_override_callback = GetTarget().GetArchitecture().GetStopInfoOverrideCallback(); // Target was stopped at entry as was intended. Need to notify the // listeners // about it. if (state == eStateStopped && launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) HandlePrivateEvent(event_sp); } else if (state == eStateExited) { // We exited while trying to launch somehow. Don't call DidLaunch // as that's // not likely to work, and return an invalid pid. HandlePrivateEvent(event_sp); } } } } else { error.SetErrorStringWithFormat("file doesn't exist: '%s'", local_exec_file_path); } } return error; } Status Process::LoadCore() { Status error = DoLoadCore(); if (error.Success()) { ListenerSP listener_sp( Listener::MakeListener("lldb.process.load_core_listener")); HijackProcessEvents(listener_sp); if (PrivateStateThreadIsValid()) ResumePrivateStateThread(); else StartPrivateStateThread(); DynamicLoader *dyld = GetDynamicLoader(); if (dyld) dyld->DidAttach(); GetJITLoaders().DidAttach(); SystemRuntime *system_runtime = GetSystemRuntime(); if (system_runtime) system_runtime->DidAttach(); if (!m_os_ap) LoadOperatingSystemPlugin(false); // We successfully loaded a core file, now pretend we stopped so we can // show all of the threads in the core file and explore the crashed // state. SetPrivateState(eStateStopped); // Wait indefinitely for a stopped event since we just posted one above... lldb::EventSP event_sp; listener_sp->GetEvent(event_sp, llvm::None); StateType state = ProcessEventData::GetStateFromEvent(event_sp.get()); if (!StateIsStoppedState(state, false)) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::Halt() failed to stop, state is: %s", StateAsCString(state)); error.SetErrorString( "Did not get stopped event after loading the core file."); } RestoreProcessEvents(); } return error; } DynamicLoader *Process::GetDynamicLoader() { if (!m_dyld_ap) m_dyld_ap.reset(DynamicLoader::FindPlugin(this, nullptr)); return m_dyld_ap.get(); } const lldb::DataBufferSP Process::GetAuxvData() { return DataBufferSP(); } JITLoaderList &Process::GetJITLoaders() { if (!m_jit_loaders_ap) { m_jit_loaders_ap.reset(new JITLoaderList()); JITLoader::LoadPlugins(this, *m_jit_loaders_ap); } return *m_jit_loaders_ap; } SystemRuntime *Process::GetSystemRuntime() { if (!m_system_runtime_ap) m_system_runtime_ap.reset(SystemRuntime::FindPlugin(this)); return m_system_runtime_ap.get(); } Process::AttachCompletionHandler::AttachCompletionHandler(Process *process, uint32_t exec_count) : NextEventAction(process), m_exec_count(exec_count) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf( "Process::AttachCompletionHandler::%s process=%p, exec_count=%" PRIu32, __FUNCTION__, static_cast(process), exec_count); } Process::NextEventAction::EventActionResult Process::AttachCompletionHandler::PerformAction(lldb::EventSP &event_sp) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); StateType state = ProcessEventData::GetStateFromEvent(event_sp.get()); if (log) log->Printf( "Process::AttachCompletionHandler::%s called with state %s (%d)", __FUNCTION__, StateAsCString(state), static_cast(state)); switch (state) { case eStateAttaching: return eEventActionSuccess; case eStateRunning: case eStateConnected: return eEventActionRetry; case eStateStopped: case eStateCrashed: // During attach, prior to sending the eStateStopped event, // lldb_private::Process subclasses must set the new process ID. assert(m_process->GetID() != LLDB_INVALID_PROCESS_ID); // We don't want these events to be reported, so go set the ShouldReportStop // here: m_process->GetThreadList().SetShouldReportStop(eVoteNo); if (m_exec_count > 0) { --m_exec_count; if (log) log->Printf("Process::AttachCompletionHandler::%s state %s: reduced " "remaining exec count to %" PRIu32 ", requesting resume", __FUNCTION__, StateAsCString(state), m_exec_count); RequestResume(); return eEventActionRetry; } else { if (log) log->Printf("Process::AttachCompletionHandler::%s state %s: no more " "execs expected to start, continuing with attach", __FUNCTION__, StateAsCString(state)); m_process->CompleteAttach(); return eEventActionSuccess; } break; default: case eStateExited: case eStateInvalid: break; } m_exit_string.assign("No valid Process"); return eEventActionExit; } Process::NextEventAction::EventActionResult Process::AttachCompletionHandler::HandleBeingInterrupted() { return eEventActionSuccess; } const char *Process::AttachCompletionHandler::GetExitString() { return m_exit_string.c_str(); } ListenerSP ProcessAttachInfo::GetListenerForProcess(Debugger &debugger) { if (m_listener_sp) return m_listener_sp; else return debugger.GetListener(); } Status Process::Attach(ProcessAttachInfo &attach_info) { m_abi_sp.reset(); m_process_input_reader.reset(); m_dyld_ap.reset(); m_jit_loaders_ap.reset(); m_system_runtime_ap.reset(); m_os_ap.reset(); m_stop_info_override_callback = nullptr; lldb::pid_t attach_pid = attach_info.GetProcessID(); Status error; if (attach_pid == LLDB_INVALID_PROCESS_ID) { char process_name[PATH_MAX]; if (attach_info.GetExecutableFile().GetPath(process_name, sizeof(process_name))) { const bool wait_for_launch = attach_info.GetWaitForLaunch(); if (wait_for_launch) { error = WillAttachToProcessWithName(process_name, wait_for_launch); if (error.Success()) { if (m_public_run_lock.TrySetRunning()) { m_should_detach = true; const bool restarted = false; SetPublicState(eStateAttaching, restarted); // Now attach using these arguments. error = DoAttachToProcessWithName(process_name, attach_info); } else { // This shouldn't happen error.SetErrorString("failed to acquire process run lock"); } if (error.Fail()) { if (GetID() != LLDB_INVALID_PROCESS_ID) { SetID(LLDB_INVALID_PROCESS_ID); if (error.AsCString() == nullptr) error.SetErrorString("attach failed"); SetExitStatus(-1, error.AsCString()); } } else { SetNextEventAction(new Process::AttachCompletionHandler( this, attach_info.GetResumeCount())); StartPrivateStateThread(); } return error; } } else { ProcessInstanceInfoList process_infos; PlatformSP platform_sp(GetTarget().GetPlatform()); if (platform_sp) { ProcessInstanceInfoMatch match_info; match_info.GetProcessInfo() = attach_info; match_info.SetNameMatchType(NameMatch::Equals); platform_sp->FindProcesses(match_info, process_infos); const uint32_t num_matches = process_infos.GetSize(); if (num_matches == 1) { attach_pid = process_infos.GetProcessIDAtIndex(0); // Fall through and attach using the above process ID } else { match_info.GetProcessInfo().GetExecutableFile().GetPath( process_name, sizeof(process_name)); if (num_matches > 1) { StreamString s; ProcessInstanceInfo::DumpTableHeader(s, platform_sp.get(), true, false); for (size_t i = 0; i < num_matches; i++) { process_infos.GetProcessInfoAtIndex(i).DumpAsTableRow( s, platform_sp.get(), true, false); } error.SetErrorStringWithFormat( "more than one process named %s:\n%s", process_name, s.GetData()); } else error.SetErrorStringWithFormat( "could not find a process named %s", process_name); } } else { error.SetErrorString( "invalid platform, can't find processes by name"); return error; } } } else { error.SetErrorString("invalid process name"); } } if (attach_pid != LLDB_INVALID_PROCESS_ID) { error = WillAttachToProcessWithID(attach_pid); if (error.Success()) { if (m_public_run_lock.TrySetRunning()) { // Now attach using these arguments. m_should_detach = true; const bool restarted = false; SetPublicState(eStateAttaching, restarted); error = DoAttachToProcessWithID(attach_pid, attach_info); } else { // This shouldn't happen error.SetErrorString("failed to acquire process run lock"); } if (error.Success()) { SetNextEventAction(new Process::AttachCompletionHandler( this, attach_info.GetResumeCount())); StartPrivateStateThread(); } else { if (GetID() != LLDB_INVALID_PROCESS_ID) SetID(LLDB_INVALID_PROCESS_ID); const char *error_string = error.AsCString(); if (error_string == nullptr) error_string = "attach failed"; SetExitStatus(-1, error_string); } } } return error; } void Process::CompleteAttach() { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_TARGET)); if (log) log->Printf("Process::%s()", __FUNCTION__); // Let the process subclass figure out at much as it can about the process // before we go looking for a dynamic loader plug-in. ArchSpec process_arch; DidAttach(process_arch); if (process_arch.IsValid()) { GetTarget().SetArchitecture(process_arch); if (log) { const char *triple_str = process_arch.GetTriple().getTriple().c_str(); log->Printf("Process::%s replacing process architecture with DidAttach() " "architecture: %s", __FUNCTION__, triple_str ? triple_str : ""); } } // We just attached. If we have a platform, ask it for the process // architecture, and if it isn't // the same as the one we've already set, switch architectures. PlatformSP platform_sp(GetTarget().GetPlatform()); assert(platform_sp); if (platform_sp) { const ArchSpec &target_arch = GetTarget().GetArchitecture(); if (target_arch.IsValid() && !platform_sp->IsCompatibleArchitecture(target_arch, false, nullptr)) { ArchSpec platform_arch; platform_sp = platform_sp->GetPlatformForArchitecture(target_arch, &platform_arch); if (platform_sp) { GetTarget().SetPlatform(platform_sp); GetTarget().SetArchitecture(platform_arch); if (log) log->Printf("Process::%s switching platform to %s and architecture " "to %s based on info from attach", __FUNCTION__, platform_sp->GetName().AsCString(""), platform_arch.GetTriple().getTriple().c_str()); } } else if (!process_arch.IsValid()) { ProcessInstanceInfo process_info; GetProcessInfo(process_info); const ArchSpec &process_arch = process_info.GetArchitecture(); if (process_arch.IsValid() && !GetTarget().GetArchitecture().IsExactMatch(process_arch)) { GetTarget().SetArchitecture(process_arch); if (log) log->Printf("Process::%s switching architecture to %s based on info " "the platform retrieved for pid %" PRIu64, __FUNCTION__, process_arch.GetTriple().getTriple().c_str(), GetID()); } } } // We have completed the attach, now it is time to find the dynamic loader // plug-in DynamicLoader *dyld = GetDynamicLoader(); if (dyld) { dyld->DidAttach(); if (log) { ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); log->Printf("Process::%s after DynamicLoader::DidAttach(), target " "executable is %s (using %s plugin)", __FUNCTION__, exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str() : "", dyld->GetPluginName().AsCString("")); } } GetJITLoaders().DidAttach(); SystemRuntime *system_runtime = GetSystemRuntime(); if (system_runtime) { system_runtime->DidAttach(); if (log) { ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); log->Printf("Process::%s after SystemRuntime::DidAttach(), target " "executable is %s (using %s plugin)", __FUNCTION__, exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str() : "", system_runtime->GetPluginName().AsCString("")); } } if (!m_os_ap) LoadOperatingSystemPlugin(false); // Figure out which one is the executable, and set that in our target: const ModuleList &target_modules = GetTarget().GetImages(); std::lock_guard guard(target_modules.GetMutex()); size_t num_modules = target_modules.GetSize(); ModuleSP new_executable_module_sp; for (size_t i = 0; i < num_modules; i++) { ModuleSP module_sp(target_modules.GetModuleAtIndexUnlocked(i)); if (module_sp && module_sp->IsExecutable()) { if (GetTarget().GetExecutableModulePointer() != module_sp.get()) new_executable_module_sp = module_sp; break; } } if (new_executable_module_sp) { GetTarget().SetExecutableModule(new_executable_module_sp, false); if (log) { ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); log->Printf( "Process::%s after looping through modules, target executable is %s", __FUNCTION__, exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str() : ""); } } m_stop_info_override_callback = process_arch.GetStopInfoOverrideCallback(); } Status Process::ConnectRemote(Stream *strm, llvm::StringRef remote_url) { m_abi_sp.reset(); m_process_input_reader.reset(); // Find the process and its architecture. Make sure it matches the // architecture of the current Target, and if not adjust it. Status error(DoConnectRemote(strm, remote_url)); if (error.Success()) { if (GetID() != LLDB_INVALID_PROCESS_ID) { EventSP event_sp; StateType state = WaitForProcessStopPrivate(event_sp, llvm::None); if (state == eStateStopped || state == eStateCrashed) { // If we attached and actually have a process on the other end, then // this ended up being the equivalent of an attach. CompleteAttach(); // This delays passing the stopped event to listeners till // CompleteAttach gets a chance to complete... HandlePrivateEvent(event_sp); } } if (PrivateStateThreadIsValid()) ResumePrivateStateThread(); else StartPrivateStateThread(); } return error; } Status Process::PrivateResume() { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_STEP)); if (log) log->Printf("Process::PrivateResume() m_stop_id = %u, public state: %s " "private state: %s", m_mod_id.GetStopID(), StateAsCString(m_public_state.GetValue()), StateAsCString(m_private_state.GetValue())); // If signals handing status changed we might want to update // our signal filters before resuming. UpdateAutomaticSignalFiltering(); Status error(WillResume()); // Tell the process it is about to resume before the thread list if (error.Success()) { // Now let the thread list know we are about to resume so it // can let all of our threads know that they are about to be // resumed. Threads will each be called with // Thread::WillResume(StateType) where StateType contains the state // that they are supposed to have when the process is resumed // (suspended/running/stepping). Threads should also check // their resume signal in lldb::Thread::GetResumeSignal() // to see if they are supposed to start back up with a signal. if (m_thread_list.WillResume()) { // Last thing, do the PreResumeActions. if (!RunPreResumeActions()) { error.SetErrorStringWithFormat( "Process::PrivateResume PreResumeActions failed, not resuming."); } else { m_mod_id.BumpResumeID(); error = DoResume(); if (error.Success()) { DidResume(); m_thread_list.DidResume(); if (log) log->Printf("Process thinks the process has resumed."); } } } else { // Somebody wanted to run without running (e.g. we were faking a step from // one frame of a set of inlined // frames that share the same PC to another.) So generate a continue & a // stopped event, // and let the world handle them. if (log) log->Printf( "Process::PrivateResume() asked to simulate a start & stop."); SetPrivateState(eStateRunning); SetPrivateState(eStateStopped); } } else if (log) log->Printf("Process::PrivateResume() got an error \"%s\".", error.AsCString("")); return error; } Status Process::Halt(bool clear_thread_plans, bool use_run_lock) { if (!StateIsRunningState(m_public_state.GetValue())) return Status("Process is not running."); // Don't clear the m_clear_thread_plans_on_stop, only set it to true if // in case it was already set and some thread plan logic calls halt on its // own. m_clear_thread_plans_on_stop |= clear_thread_plans; ListenerSP halt_listener_sp( Listener::MakeListener("lldb.process.halt_listener")); HijackProcessEvents(halt_listener_sp); EventSP event_sp; SendAsyncInterrupt(); if (m_public_state.GetValue() == eStateAttaching) { // Don't hijack and eat the eStateExited as the code that was doing // the attach will be waiting for this event... RestoreProcessEvents(); SetExitStatus(SIGKILL, "Cancelled async attach."); Destroy(false); return Status(); } // Wait for 10 second for the process to stop. StateType state = WaitForProcessToStop( seconds(10), &event_sp, true, halt_listener_sp, nullptr, use_run_lock); RestoreProcessEvents(); if (state == eStateInvalid || !event_sp) { // We timed out and didn't get a stop event... return Status("Halt timed out. State = %s", StateAsCString(GetState())); } BroadcastEvent(event_sp); return Status(); } Status Process::StopForDestroyOrDetach(lldb::EventSP &exit_event_sp) { Status error; // Check both the public & private states here. If we're hung evaluating an // expression, for instance, then // the public state will be stopped, but we still need to interrupt. if (m_public_state.GetValue() == eStateRunning || m_private_state.GetValue() == eStateRunning) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::%s() About to stop.", __FUNCTION__); ListenerSP listener_sp( Listener::MakeListener("lldb.Process.StopForDestroyOrDetach.hijack")); HijackProcessEvents(listener_sp); SendAsyncInterrupt(); // Consume the interrupt event. StateType state = WaitForProcessToStop(seconds(10), &exit_event_sp, true, listener_sp); RestoreProcessEvents(); // If the process exited while we were waiting for it to stop, put the // exited event into // the shared pointer passed in and return. Our caller doesn't need to do // anything else, since // they don't have a process anymore... if (state == eStateExited || m_private_state.GetValue() == eStateExited) { if (log) log->Printf("Process::%s() Process exited while waiting to stop.", __FUNCTION__); return error; } else exit_event_sp.reset(); // It is ok to consume any non-exit stop events if (state != eStateStopped) { if (log) log->Printf("Process::%s() failed to stop, state is: %s", __FUNCTION__, StateAsCString(state)); // If we really couldn't stop the process then we should just error out // here, but if the // lower levels just bobbled sending the event and we really are stopped, // then continue on. StateType private_state = m_private_state.GetValue(); if (private_state != eStateStopped) { return Status( "Attempt to stop the target in order to detach timed out. " "State = %s", StateAsCString(GetState())); } } } return error; } Status Process::Detach(bool keep_stopped) { EventSP exit_event_sp; Status error; m_destroy_in_process = true; error = WillDetach(); if (error.Success()) { if (DetachRequiresHalt()) { error = StopForDestroyOrDetach(exit_event_sp); if (!error.Success()) { m_destroy_in_process = false; return error; } else if (exit_event_sp) { // We shouldn't need to do anything else here. There's no process left // to detach from... StopPrivateStateThread(); m_destroy_in_process = false; return error; } } m_thread_list.DiscardThreadPlans(); DisableAllBreakpointSites(); error = DoDetach(keep_stopped); if (error.Success()) { DidDetach(); StopPrivateStateThread(); } else { return error; } } m_destroy_in_process = false; // If we exited when we were waiting for a process to stop, then // forward the event here so we don't lose the event if (exit_event_sp) { // Directly broadcast our exited event because we shut down our // private state thread above BroadcastEvent(exit_event_sp); } // If we have been interrupted (to kill us) in the middle of running, we may // not end up propagating // the last events through the event system, in which case we might strand the // write lock. Unlock // it here so when we do to tear down the process we don't get an error // destroying the lock. m_public_run_lock.SetStopped(); return error; } Status Process::Destroy(bool force_kill) { // Tell ourselves we are in the process of destroying the process, so that we // don't do any unnecessary work // that might hinder the destruction. Remember to set this back to false when // we are done. That way if the attempt // failed and the process stays around for some reason it won't be in a // confused state. if (force_kill) m_should_detach = false; if (GetShouldDetach()) { // FIXME: This will have to be a process setting: bool keep_stopped = false; Detach(keep_stopped); } m_destroy_in_process = true; Status error(WillDestroy()); if (error.Success()) { EventSP exit_event_sp; if (DestroyRequiresHalt()) { error = StopForDestroyOrDetach(exit_event_sp); } if (m_public_state.GetValue() != eStateRunning) { // Ditch all thread plans, and remove all our breakpoints: in case we have // to restart the target to // kill it, we don't want it hitting a breakpoint... // Only do this if we've stopped, however, since if we didn't manage to // halt it above, then // we're not going to have much luck doing this now. m_thread_list.DiscardThreadPlans(); DisableAllBreakpointSites(); } error = DoDestroy(); if (error.Success()) { DidDestroy(); StopPrivateStateThread(); } m_stdio_communication.Disconnect(); m_stdio_communication.StopReadThread(); m_stdin_forward = false; if (m_process_input_reader) { m_process_input_reader->SetIsDone(true); m_process_input_reader->Cancel(); m_process_input_reader.reset(); } // If we exited when we were waiting for a process to stop, then // forward the event here so we don't lose the event if (exit_event_sp) { // Directly broadcast our exited event because we shut down our // private state thread above BroadcastEvent(exit_event_sp); } // If we have been interrupted (to kill us) in the middle of running, we may // not end up propagating // the last events through the event system, in which case we might strand // the write lock. Unlock // it here so when we do to tear down the process we don't get an error // destroying the lock. m_public_run_lock.SetStopped(); } m_destroy_in_process = false; return error; } Status Process::Signal(int signal) { Status error(WillSignal()); if (error.Success()) { error = DoSignal(signal); if (error.Success()) DidSignal(); } return error; } void Process::SetUnixSignals(UnixSignalsSP &&signals_sp) { assert(signals_sp && "null signals_sp"); m_unix_signals_sp = signals_sp; } const lldb::UnixSignalsSP &Process::GetUnixSignals() { assert(m_unix_signals_sp && "null m_unix_signals_sp"); return m_unix_signals_sp; } lldb::ByteOrder Process::GetByteOrder() const { return GetTarget().GetArchitecture().GetByteOrder(); } uint32_t Process::GetAddressByteSize() const { return GetTarget().GetArchitecture().GetAddressByteSize(); } bool Process::ShouldBroadcastEvent(Event *event_ptr) { const StateType state = Process::ProcessEventData::GetStateFromEvent(event_ptr); bool return_value = true; Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EVENTS | LIBLLDB_LOG_PROCESS)); switch (state) { case eStateDetached: case eStateExited: case eStateUnloaded: m_stdio_communication.SynchronizeWithReadThread(); m_stdio_communication.Disconnect(); m_stdio_communication.StopReadThread(); m_stdin_forward = false; LLVM_FALLTHROUGH; case eStateConnected: case eStateAttaching: case eStateLaunching: // These events indicate changes in the state of the debugging session, // always report them. return_value = true; break; case eStateInvalid: // We stopped for no apparent reason, don't report it. return_value = false; break; case eStateRunning: case eStateStepping: // If we've started the target running, we handle the cases where we // are already running and where there is a transition from stopped to // running differently. // running -> running: Automatically suppress extra running events // stopped -> running: Report except when there is one or more no votes // and no yes votes. SynchronouslyNotifyStateChanged(state); if (m_force_next_event_delivery) return_value = true; else { switch (m_last_broadcast_state) { case eStateRunning: case eStateStepping: // We always suppress multiple runnings with no PUBLIC stop in between. return_value = false; break; default: // TODO: make this work correctly. For now always report // run if we aren't running so we don't miss any running // events. If I run the lldb/test/thread/a.out file and // break at main.cpp:58, run and hit the breakpoints on // multiple threads, then somehow during the stepping over // of all breakpoints no run gets reported. // This is a transition from stop to run. switch (m_thread_list.ShouldReportRun(event_ptr)) { case eVoteYes: case eVoteNoOpinion: return_value = true; break; case eVoteNo: return_value = false; break; } break; } } break; case eStateStopped: case eStateCrashed: case eStateSuspended: // We've stopped. First see if we're going to restart the target. // If we are going to stop, then we always broadcast the event. // If we aren't going to stop, let the thread plans decide if we're going to // report this event. // If no thread has an opinion, we don't report it. m_stdio_communication.SynchronizeWithReadThread(); RefreshStateAfterStop(); if (ProcessEventData::GetInterruptedFromEvent(event_ptr)) { if (log) log->Printf("Process::ShouldBroadcastEvent (%p) stopped due to an " "interrupt, state: %s", static_cast(event_ptr), StateAsCString(state)); // Even though we know we are going to stop, we should let the threads // have a look at the stop, // so they can properly set their state. m_thread_list.ShouldStop(event_ptr); return_value = true; } else { bool was_restarted = ProcessEventData::GetRestartedFromEvent(event_ptr); bool should_resume = false; // It makes no sense to ask "ShouldStop" if we've already been // restarted... // Asking the thread list is also not likely to go well, since we are // running again. // So in that case just report the event. if (!was_restarted) should_resume = !m_thread_list.ShouldStop(event_ptr); if (was_restarted || should_resume || m_resume_requested) { Vote stop_vote = m_thread_list.ShouldReportStop(event_ptr); if (log) log->Printf("Process::ShouldBroadcastEvent: should_resume: %i state: " "%s was_restarted: %i stop_vote: %d.", should_resume, StateAsCString(state), was_restarted, stop_vote); switch (stop_vote) { case eVoteYes: return_value = true; break; case eVoteNoOpinion: case eVoteNo: return_value = false; break; } if (!was_restarted) { if (log) log->Printf("Process::ShouldBroadcastEvent (%p) Restarting process " "from state: %s", static_cast(event_ptr), StateAsCString(state)); ProcessEventData::SetRestartedInEvent(event_ptr, true); PrivateResume(); } } else { return_value = true; SynchronouslyNotifyStateChanged(state); } } break; } // Forcing the next event delivery is a one shot deal. So reset it here. m_force_next_event_delivery = false; // We do some coalescing of events (for instance two consecutive running // events get coalesced.) // But we only coalesce against events we actually broadcast. So we use // m_last_broadcast_state // to track that. NB - you can't use "m_public_state.GetValue()" for that // purpose, as was originally done, // because the PublicState reflects the last event pulled off the queue, and // there may be several // events stacked up on the queue unserviced. So the PublicState may not // reflect the last broadcasted event // yet. m_last_broadcast_state gets updated here. if (return_value) m_last_broadcast_state = state; if (log) log->Printf("Process::ShouldBroadcastEvent (%p) => new state: %s, last " "broadcast state: %s - %s", static_cast(event_ptr), StateAsCString(state), StateAsCString(m_last_broadcast_state), return_value ? "YES" : "NO"); return return_value; } bool Process::StartPrivateStateThread(bool is_secondary_thread) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS)); bool already_running = PrivateStateThreadIsValid(); if (log) log->Printf("Process::%s()%s ", __FUNCTION__, already_running ? " already running" : " starting private state thread"); if (!is_secondary_thread && already_running) return true; // Create a thread that watches our internal state and controls which // events make it to clients (into the DCProcess event queue). char thread_name[1024]; uint32_t max_len = llvm::get_max_thread_name_length(); if (max_len > 0 && max_len <= 30) { // On platforms with abbreviated thread name lengths, choose thread names // that fit within the limit. if (already_running) snprintf(thread_name, sizeof(thread_name), "intern-state-OV"); else snprintf(thread_name, sizeof(thread_name), "intern-state"); } else { if (already_running) snprintf(thread_name, sizeof(thread_name), "", GetID()); else snprintf(thread_name, sizeof(thread_name), "", GetID()); } // Create the private state thread, and start it running. PrivateStateThreadArgs *args_ptr = new PrivateStateThreadArgs(this, is_secondary_thread); m_private_state_thread = ThreadLauncher::LaunchThread(thread_name, Process::PrivateStateThread, (void *)args_ptr, nullptr, 8 * 1024 * 1024); if (m_private_state_thread.IsJoinable()) { ResumePrivateStateThread(); return true; } else return false; } void Process::PausePrivateStateThread() { ControlPrivateStateThread(eBroadcastInternalStateControlPause); } void Process::ResumePrivateStateThread() { ControlPrivateStateThread(eBroadcastInternalStateControlResume); } void Process::StopPrivateStateThread() { if (m_private_state_thread.IsJoinable()) ControlPrivateStateThread(eBroadcastInternalStateControlStop); else { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf( "Went to stop the private state thread, but it was already invalid."); } } void Process::ControlPrivateStateThread(uint32_t signal) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); assert(signal == eBroadcastInternalStateControlStop || signal == eBroadcastInternalStateControlPause || signal == eBroadcastInternalStateControlResume); if (log) log->Printf("Process::%s (signal = %d)", __FUNCTION__, signal); // Signal the private state thread if (m_private_state_thread.IsJoinable()) { // Broadcast the event. // It is important to do this outside of the if below, because // it's possible that the thread state is invalid but that the // thread is waiting on a control event instead of simply being // on its way out (this should not happen, but it apparently can). if (log) log->Printf("Sending control event of type: %d.", signal); std::shared_ptr event_receipt_sp(new EventDataReceipt()); m_private_state_control_broadcaster.BroadcastEvent(signal, event_receipt_sp); // Wait for the event receipt or for the private state thread to exit bool receipt_received = false; if (PrivateStateThreadIsValid()) { while (!receipt_received) { bool timed_out = false; // Check for a receipt for 2 seconds and then check if the private state // thread is still around. receipt_received = event_receipt_sp->WaitForEventReceived( std::chrono::seconds(2), &timed_out); if (!receipt_received) { // Check if the private state thread is still around. If it isn't then // we are done waiting if (!PrivateStateThreadIsValid()) break; // Private state thread exited or is exiting, we are done } } } if (signal == eBroadcastInternalStateControlStop) { thread_result_t result = NULL; m_private_state_thread.Join(&result); m_private_state_thread.Reset(); } } else { if (log) log->Printf( "Private state thread already dead, no need to signal it to stop."); } } void Process::SendAsyncInterrupt() { if (PrivateStateThreadIsValid()) m_private_state_broadcaster.BroadcastEvent(Process::eBroadcastBitInterrupt, nullptr); else BroadcastEvent(Process::eBroadcastBitInterrupt, nullptr); } void Process::HandlePrivateEvent(EventSP &event_sp) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); m_resume_requested = false; const StateType new_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); // First check to see if anybody wants a shot at this event: if (m_next_event_action_ap) { NextEventAction::EventActionResult action_result = m_next_event_action_ap->PerformAction(event_sp); if (log) log->Printf("Ran next event action, result was %d.", action_result); switch (action_result) { case NextEventAction::eEventActionSuccess: SetNextEventAction(nullptr); break; case NextEventAction::eEventActionRetry: break; case NextEventAction::eEventActionExit: // Handle Exiting Here. If we already got an exited event, // we should just propagate it. Otherwise, swallow this event, // and set our state to exit so the next event will kill us. if (new_state != eStateExited) { // FIXME: should cons up an exited event, and discard this one. SetExitStatus(0, m_next_event_action_ap->GetExitString()); SetNextEventAction(nullptr); return; } SetNextEventAction(nullptr); break; } } // See if we should broadcast this state to external clients? const bool should_broadcast = ShouldBroadcastEvent(event_sp.get()); if (should_broadcast) { const bool is_hijacked = IsHijackedForEvent(eBroadcastBitStateChanged); if (log) { log->Printf("Process::%s (pid = %" PRIu64 ") broadcasting new state %s (old state %s) to %s", __FUNCTION__, GetID(), StateAsCString(new_state), StateAsCString(GetState()), is_hijacked ? "hijacked" : "public"); } Process::ProcessEventData::SetUpdateStateOnRemoval(event_sp.get()); if (StateIsRunningState(new_state)) { // Only push the input handler if we aren't fowarding events, // as this means the curses GUI is in use... // Or don't push it if we are launching since it will come up stopped. if (!GetTarget().GetDebugger().IsForwardingEvents() && new_state != eStateLaunching && new_state != eStateAttaching) { PushProcessIOHandler(); m_iohandler_sync.SetValue(m_iohandler_sync.GetValue() + 1, eBroadcastAlways); if (log) log->Printf("Process::%s updated m_iohandler_sync to %d", __FUNCTION__, m_iohandler_sync.GetValue()); } } else if (StateIsStoppedState(new_state, false)) { if (!Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) { // If the lldb_private::Debugger is handling the events, we don't // want to pop the process IOHandler here, we want to do it when // we receive the stopped event so we can carefully control when // the process IOHandler is popped because when we stop we want to // display some text stating how and why we stopped, then maybe some // process/thread/frame info, and then we want the "(lldb) " prompt // to show up. If we pop the process IOHandler here, then we will // cause the command interpreter to become the top IOHandler after // the process pops off and it will update its prompt right away... // See the Debugger.cpp file where it calls the function as // "process_sp->PopProcessIOHandler()" to see where I am talking about. // Otherwise we end up getting overlapping "(lldb) " prompts and // garbled output. // // If we aren't handling the events in the debugger (which is indicated // by "m_target.GetDebugger().IsHandlingEvents()" returning false) or we // are hijacked, then we always pop the process IO handler manually. // Hijacking happens when the internal process state thread is running // thread plans, or when commands want to run in synchronous mode // and they call "process->WaitForProcessToStop()". An example of // something // that will hijack the events is a simple expression: // // (lldb) expr (int)puts("hello") // // This will cause the internal process state thread to resume and halt // the process (and _it_ will hijack the eBroadcastBitStateChanged // events) and we do need the IO handler to be pushed and popped // correctly. if (is_hijacked || !GetTarget().GetDebugger().IsHandlingEvents()) PopProcessIOHandler(); } } BroadcastEvent(event_sp); } else { if (log) { log->Printf( "Process::%s (pid = %" PRIu64 ") suppressing state %s (old state %s): should_broadcast == false", __FUNCTION__, GetID(), StateAsCString(new_state), StateAsCString(GetState())); } } } Status Process::HaltPrivate() { EventSP event_sp; Status error(WillHalt()); if (error.Fail()) return error; // Ask the process subclass to actually halt our process bool caused_stop; error = DoHalt(caused_stop); DidHalt(); return error; } thread_result_t Process::PrivateStateThread(void *arg) { std::unique_ptr args_up( static_cast(arg)); thread_result_t result = args_up->process->RunPrivateStateThread(args_up->is_secondary_thread); return result; } thread_result_t Process::RunPrivateStateThread(bool is_secondary_thread) { bool control_only = true; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::%s (arg = %p, pid = %" PRIu64 ") thread starting...", __FUNCTION__, static_cast(this), GetID()); bool exit_now = false; bool interrupt_requested = false; while (!exit_now) { EventSP event_sp; GetEventsPrivate(event_sp, llvm::None, control_only); if (event_sp->BroadcasterIs(&m_private_state_control_broadcaster)) { if (log) log->Printf("Process::%s (arg = %p, pid = %" PRIu64 ") got a control event: %d", __FUNCTION__, static_cast(this), GetID(), event_sp->GetType()); switch (event_sp->GetType()) { case eBroadcastInternalStateControlStop: exit_now = true; break; // doing any internal state management below case eBroadcastInternalStateControlPause: control_only = true; break; case eBroadcastInternalStateControlResume: control_only = false; break; } continue; } else if (event_sp->GetType() == eBroadcastBitInterrupt) { if (m_public_state.GetValue() == eStateAttaching) { if (log) log->Printf("Process::%s (arg = %p, pid = %" PRIu64 ") woke up with an interrupt while attaching - " "forwarding interrupt.", __FUNCTION__, static_cast(this), GetID()); BroadcastEvent(eBroadcastBitInterrupt, nullptr); } else if (StateIsRunningState(m_last_broadcast_state)) { if (log) log->Printf("Process::%s (arg = %p, pid = %" PRIu64 ") woke up with an interrupt - Halting.", __FUNCTION__, static_cast(this), GetID()); Status error = HaltPrivate(); if (error.Fail() && log) log->Printf("Process::%s (arg = %p, pid = %" PRIu64 ") failed to halt the process: %s", __FUNCTION__, static_cast(this), GetID(), error.AsCString()); // Halt should generate a stopped event. Make a note of the fact that we // were // doing the interrupt, so we can set the interrupted flag after we // receive the // event. We deliberately set this to true even if HaltPrivate failed, // so that we // can interrupt on the next natural stop. interrupt_requested = true; } else { // This can happen when someone (e.g. Process::Halt) sees that we are // running and // sends an interrupt request, but the process actually stops before we // receive // it. In that case, we can just ignore the request. We use // m_last_broadcast_state, because the Stopped event may not have been // popped of // the event queue yet, which is when the public state gets updated. if (log) log->Printf( "Process::%s ignoring interrupt as we have already stopped.", __FUNCTION__); } continue; } const StateType internal_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); if (internal_state != eStateInvalid) { if (m_clear_thread_plans_on_stop && StateIsStoppedState(internal_state, true)) { m_clear_thread_plans_on_stop = false; m_thread_list.DiscardThreadPlans(); } if (interrupt_requested) { if (StateIsStoppedState(internal_state, true)) { // We requested the interrupt, so mark this as such in the stop event // so // clients can tell an interrupted process from a natural stop ProcessEventData::SetInterruptedInEvent(event_sp.get(), true); interrupt_requested = false; } else if (log) { log->Printf("Process::%s interrupt_requested, but a non-stopped " "state '%s' received.", __FUNCTION__, StateAsCString(internal_state)); } } HandlePrivateEvent(event_sp); } if (internal_state == eStateInvalid || internal_state == eStateExited || internal_state == eStateDetached) { if (log) log->Printf("Process::%s (arg = %p, pid = %" PRIu64 ") about to exit with internal state %s...", __FUNCTION__, static_cast(this), GetID(), StateAsCString(internal_state)); break; } } // Verify log is still enabled before attempting to write to it... if (log) log->Printf("Process::%s (arg = %p, pid = %" PRIu64 ") thread exiting...", __FUNCTION__, static_cast(this), GetID()); // If we are a secondary thread, then the primary thread we are working for // will have already // acquired the public_run_lock, and isn't done with what it was doing yet, so // don't // try to change it on the way out. if (!is_secondary_thread) m_public_run_lock.SetStopped(); return NULL; } //------------------------------------------------------------------ // Process Event Data //------------------------------------------------------------------ Process::ProcessEventData::ProcessEventData() : EventData(), m_process_wp(), m_state(eStateInvalid), m_restarted(false), m_update_state(0), m_interrupted(false) {} Process::ProcessEventData::ProcessEventData(const ProcessSP &process_sp, StateType state) : EventData(), m_process_wp(), m_state(state), m_restarted(false), m_update_state(0), m_interrupted(false) { if (process_sp) m_process_wp = process_sp; } Process::ProcessEventData::~ProcessEventData() = default; const ConstString &Process::ProcessEventData::GetFlavorString() { static ConstString g_flavor("Process::ProcessEventData"); return g_flavor; } const ConstString &Process::ProcessEventData::GetFlavor() const { return ProcessEventData::GetFlavorString(); } void Process::ProcessEventData::DoOnRemoval(Event *event_ptr) { ProcessSP process_sp(m_process_wp.lock()); if (!process_sp) return; // This function gets called twice for each event, once when the event gets // pulled // off of the private process event queue, and then any number of times, first // when it gets pulled off of // the public event queue, then other times when we're pretending that this is // where we stopped at the // end of expression evaluation. m_update_state is used to distinguish these // three cases; it is 0 when we're just pulling it off for private handling, // and > 1 for expression evaluation, and we don't want to do the breakpoint // command handling then. if (m_update_state != 1) return; process_sp->SetPublicState( m_state, Process::ProcessEventData::GetRestartedFromEvent(event_ptr)); if (m_state == eStateStopped && !m_restarted) { // Let process subclasses know we are about to do a public stop and // do anything they might need to in order to speed up register and // memory accesses. process_sp->WillPublicStop(); } // If this is a halt event, even if the halt stopped with some reason other // than a plain interrupt (e.g. we had // already stopped for a breakpoint when the halt request came through) don't // do the StopInfo actions, as they may // end up restarting the process. if (m_interrupted) return; // If we're stopped and haven't restarted, then do the StopInfo actions here: if (m_state == eStateStopped && !m_restarted) { ThreadList &curr_thread_list = process_sp->GetThreadList(); uint32_t num_threads = curr_thread_list.GetSize(); uint32_t idx; // The actions might change one of the thread's stop_info's opinions about // whether we should // stop the process, so we need to query that as we go. // One other complication here, is that we try to catch any case where the // target has run (except for expressions) // and immediately exit, but if we get that wrong (which is possible) then // the thread list might have changed, and // that would cause our iteration here to crash. We could make a copy of // the thread list, but we'd really like // to also know if it has changed at all, so we make up a vector of the // thread ID's and check what we get back // against this list & bag out if anything differs. std::vector thread_index_array(num_threads); for (idx = 0; idx < num_threads; ++idx) thread_index_array[idx] = curr_thread_list.GetThreadAtIndex(idx)->GetIndexID(); // Use this to track whether we should continue from here. We will only // continue the target running if // no thread says we should stop. Of course if some thread's PerformAction // actually sets the target running, // then it doesn't matter what the other threads say... bool still_should_stop = false; // Sometimes - for instance if we have a bug in the stub we are talking to, // we stop but no thread has a // valid stop reason. In that case we should just stop, because we have no // way of telling what the right // thing to do is, and it's better to let the user decide than continue // behind their backs. bool does_anybody_have_an_opinion = false; for (idx = 0; idx < num_threads; ++idx) { curr_thread_list = process_sp->GetThreadList(); if (curr_thread_list.GetSize() != num_threads) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS)); if (log) log->Printf( "Number of threads changed from %u to %u while processing event.", num_threads, curr_thread_list.GetSize()); break; } lldb::ThreadSP thread_sp = curr_thread_list.GetThreadAtIndex(idx); if (thread_sp->GetIndexID() != thread_index_array[idx]) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS)); if (log) log->Printf("The thread at position %u changed from %u to %u while " "processing event.", idx, thread_index_array[idx], thread_sp->GetIndexID()); break; } StopInfoSP stop_info_sp = thread_sp->GetStopInfo(); if (stop_info_sp && stop_info_sp->IsValid()) { does_anybody_have_an_opinion = true; bool this_thread_wants_to_stop; if (stop_info_sp->GetOverrideShouldStop()) { this_thread_wants_to_stop = stop_info_sp->GetOverriddenShouldStopValue(); } else { stop_info_sp->PerformAction(event_ptr); // The stop action might restart the target. If it does, then we want // to mark that in the // event so that whoever is receiving it will know to wait for the // running event and reflect // that state appropriately. // We also need to stop processing actions, since they aren't // expecting the target to be running. // FIXME: we might have run. if (stop_info_sp->HasTargetRunSinceMe()) { SetRestarted(true); break; } this_thread_wants_to_stop = stop_info_sp->ShouldStop(event_ptr); } if (!still_should_stop) still_should_stop = this_thread_wants_to_stop; } } if (!GetRestarted()) { if (!still_should_stop && does_anybody_have_an_opinion) { // We've been asked to continue, so do that here. SetRestarted(true); // Use the public resume method here, since this is just // extending a public resume. process_sp->PrivateResume(); } else { // If we didn't restart, run the Stop Hooks here: // They might also restart the target, so watch for that. process_sp->GetTarget().RunStopHooks(); if (process_sp->GetPrivateState() == eStateRunning) SetRestarted(true); } } } } void Process::ProcessEventData::Dump(Stream *s) const { ProcessSP process_sp(m_process_wp.lock()); if (process_sp) s->Printf(" process = %p (pid = %" PRIu64 "), ", static_cast(process_sp.get()), process_sp->GetID()); else s->PutCString(" process = NULL, "); s->Printf("state = %s", StateAsCString(GetState())); } const Process::ProcessEventData * Process::ProcessEventData::GetEventDataFromEvent(const Event *event_ptr) { if (event_ptr) { const EventData *event_data = event_ptr->GetData(); if (event_data && event_data->GetFlavor() == ProcessEventData::GetFlavorString()) return static_cast(event_ptr->GetData()); } return nullptr; } ProcessSP Process::ProcessEventData::GetProcessFromEvent(const Event *event_ptr) { ProcessSP process_sp; const ProcessEventData *data = GetEventDataFromEvent(event_ptr); if (data) process_sp = data->GetProcessSP(); return process_sp; } StateType Process::ProcessEventData::GetStateFromEvent(const Event *event_ptr) { const ProcessEventData *data = GetEventDataFromEvent(event_ptr); if (data == nullptr) return eStateInvalid; else return data->GetState(); } bool Process::ProcessEventData::GetRestartedFromEvent(const Event *event_ptr) { const ProcessEventData *data = GetEventDataFromEvent(event_ptr); if (data == nullptr) return false; else return data->GetRestarted(); } void Process::ProcessEventData::SetRestartedInEvent(Event *event_ptr, bool new_value) { ProcessEventData *data = const_cast(GetEventDataFromEvent(event_ptr)); if (data != nullptr) data->SetRestarted(new_value); } size_t Process::ProcessEventData::GetNumRestartedReasons(const Event *event_ptr) { ProcessEventData *data = const_cast(GetEventDataFromEvent(event_ptr)); if (data != nullptr) return data->GetNumRestartedReasons(); else return 0; } const char * Process::ProcessEventData::GetRestartedReasonAtIndex(const Event *event_ptr, size_t idx) { ProcessEventData *data = const_cast(GetEventDataFromEvent(event_ptr)); if (data != nullptr) return data->GetRestartedReasonAtIndex(idx); else return nullptr; } void Process::ProcessEventData::AddRestartedReason(Event *event_ptr, const char *reason) { ProcessEventData *data = const_cast(GetEventDataFromEvent(event_ptr)); if (data != nullptr) data->AddRestartedReason(reason); } bool Process::ProcessEventData::GetInterruptedFromEvent( const Event *event_ptr) { const ProcessEventData *data = GetEventDataFromEvent(event_ptr); if (data == nullptr) return false; else return data->GetInterrupted(); } void Process::ProcessEventData::SetInterruptedInEvent(Event *event_ptr, bool new_value) { ProcessEventData *data = const_cast(GetEventDataFromEvent(event_ptr)); if (data != nullptr) data->SetInterrupted(new_value); } bool Process::ProcessEventData::SetUpdateStateOnRemoval(Event *event_ptr) { ProcessEventData *data = const_cast(GetEventDataFromEvent(event_ptr)); if (data) { data->SetUpdateStateOnRemoval(); return true; } return false; } lldb::TargetSP Process::CalculateTarget() { return m_target_sp.lock(); } void Process::CalculateExecutionContext(ExecutionContext &exe_ctx) { exe_ctx.SetTargetPtr(&GetTarget()); exe_ctx.SetProcessPtr(this); exe_ctx.SetThreadPtr(nullptr); exe_ctx.SetFramePtr(nullptr); } // uint32_t // Process::ListProcessesMatchingName (const char *name, StringList &matches, // std::vector &pids) //{ // return 0; //} // // ArchSpec // Process::GetArchSpecForExistingProcess (lldb::pid_t pid) //{ // return Host::GetArchSpecForExistingProcess (pid); //} // // ArchSpec // Process::GetArchSpecForExistingProcess (const char *process_name) //{ // return Host::GetArchSpecForExistingProcess (process_name); //} void Process::AppendSTDOUT(const char *s, size_t len) { std::lock_guard guard(m_stdio_communication_mutex); m_stdout_data.append(s, len); BroadcastEventIfUnique(eBroadcastBitSTDOUT, new ProcessEventData(shared_from_this(), GetState())); } void Process::AppendSTDERR(const char *s, size_t len) { std::lock_guard guard(m_stdio_communication_mutex); m_stderr_data.append(s, len); BroadcastEventIfUnique(eBroadcastBitSTDERR, new ProcessEventData(shared_from_this(), GetState())); } void Process::BroadcastAsyncProfileData(const std::string &one_profile_data) { std::lock_guard guard(m_profile_data_comm_mutex); m_profile_data.push_back(one_profile_data); BroadcastEventIfUnique(eBroadcastBitProfileData, new ProcessEventData(shared_from_this(), GetState())); } void Process::BroadcastStructuredData(const StructuredData::ObjectSP &object_sp, const StructuredDataPluginSP &plugin_sp) { BroadcastEvent( eBroadcastBitStructuredData, new EventDataStructuredData(shared_from_this(), object_sp, plugin_sp)); } StructuredDataPluginSP Process::GetStructuredDataPlugin(const ConstString &type_name) const { auto find_it = m_structured_data_plugin_map.find(type_name); if (find_it != m_structured_data_plugin_map.end()) return find_it->second; else return StructuredDataPluginSP(); } size_t Process::GetAsyncProfileData(char *buf, size_t buf_size, Status &error) { std::lock_guard guard(m_profile_data_comm_mutex); if (m_profile_data.empty()) return 0; std::string &one_profile_data = m_profile_data.front(); size_t bytes_available = one_profile_data.size(); if (bytes_available > 0) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::GetProfileData (buf = %p, size = %" PRIu64 ")", static_cast(buf), static_cast(buf_size)); if (bytes_available > buf_size) { memcpy(buf, one_profile_data.c_str(), buf_size); one_profile_data.erase(0, buf_size); bytes_available = buf_size; } else { memcpy(buf, one_profile_data.c_str(), bytes_available); m_profile_data.erase(m_profile_data.begin()); } } return bytes_available; } //------------------------------------------------------------------ // Process STDIO //------------------------------------------------------------------ size_t Process::GetSTDOUT(char *buf, size_t buf_size, Status &error) { std::lock_guard guard(m_stdio_communication_mutex); size_t bytes_available = m_stdout_data.size(); if (bytes_available > 0) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::GetSTDOUT (buf = %p, size = %" PRIu64 ")", static_cast(buf), static_cast(buf_size)); if (bytes_available > buf_size) { memcpy(buf, m_stdout_data.c_str(), buf_size); m_stdout_data.erase(0, buf_size); bytes_available = buf_size; } else { memcpy(buf, m_stdout_data.c_str(), bytes_available); m_stdout_data.clear(); } } return bytes_available; } size_t Process::GetSTDERR(char *buf, size_t buf_size, Status &error) { std::lock_guard gaurd(m_stdio_communication_mutex); size_t bytes_available = m_stderr_data.size(); if (bytes_available > 0) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::GetSTDERR (buf = %p, size = %" PRIu64 ")", static_cast(buf), static_cast(buf_size)); if (bytes_available > buf_size) { memcpy(buf, m_stderr_data.c_str(), buf_size); m_stderr_data.erase(0, buf_size); bytes_available = buf_size; } else { memcpy(buf, m_stderr_data.c_str(), bytes_available); m_stderr_data.clear(); } } return bytes_available; } void Process::STDIOReadThreadBytesReceived(void *baton, const void *src, size_t src_len) { Process *process = (Process *)baton; process->AppendSTDOUT(static_cast(src), src_len); } class IOHandlerProcessSTDIO : public IOHandler { public: IOHandlerProcessSTDIO(Process *process, int write_fd) : IOHandler(process->GetTarget().GetDebugger(), IOHandler::Type::ProcessIO), m_process(process), m_write_file(write_fd, false) { m_pipe.CreateNew(false); m_read_file.SetDescriptor(GetInputFD(), false); } ~IOHandlerProcessSTDIO() override = default; // Each IOHandler gets to run until it is done. It should read data // from the "in" and place output into "out" and "err and return // when done. void Run() override { if (!m_read_file.IsValid() || !m_write_file.IsValid() || !m_pipe.CanRead() || !m_pipe.CanWrite()) { SetIsDone(true); return; } SetIsDone(false); const int read_fd = m_read_file.GetDescriptor(); TerminalState terminal_state; terminal_state.Save(read_fd, false); Terminal terminal(read_fd); terminal.SetCanonical(false); terminal.SetEcho(false); // FD_ZERO, FD_SET are not supported on windows #ifndef _WIN32 const int pipe_read_fd = m_pipe.GetReadFileDescriptor(); m_is_running = true; while (!GetIsDone()) { SelectHelper select_helper; select_helper.FDSetRead(read_fd); select_helper.FDSetRead(pipe_read_fd); Status error = select_helper.Select(); if (error.Fail()) { SetIsDone(true); } else { char ch = 0; size_t n; if (select_helper.FDIsSetRead(read_fd)) { n = 1; if (m_read_file.Read(&ch, n).Success() && n == 1) { if (m_write_file.Write(&ch, n).Fail() || n != 1) SetIsDone(true); } else SetIsDone(true); } if (select_helper.FDIsSetRead(pipe_read_fd)) { size_t bytes_read; // Consume the interrupt byte Status error = m_pipe.Read(&ch, 1, bytes_read); if (error.Success()) { switch (ch) { case 'q': SetIsDone(true); break; case 'i': if (StateIsRunningState(m_process->GetState())) m_process->SendAsyncInterrupt(); break; } } } } } m_is_running = false; #endif terminal_state.Restore(); } void Cancel() override { SetIsDone(true); // Only write to our pipe to cancel if we are in // IOHandlerProcessSTDIO::Run(). // We can end up with a python command that is being run from the command // interpreter: // // (lldb) step_process_thousands_of_times // // In this case the command interpreter will be in the middle of handling // the command and if the process pushes and pops the IOHandler thousands // of times, we can end up writing to m_pipe without ever consuming the // bytes from the pipe in IOHandlerProcessSTDIO::Run() and end up // deadlocking when the pipe gets fed up and blocks until data is consumed. if (m_is_running) { char ch = 'q'; // Send 'q' for quit size_t bytes_written = 0; m_pipe.Write(&ch, 1, bytes_written); } } bool Interrupt() override { // Do only things that are safe to do in an interrupt context (like in // a SIGINT handler), like write 1 byte to a file descriptor. This will // interrupt the IOHandlerProcessSTDIO::Run() and we can look at the byte // that was written to the pipe and then call // m_process->SendAsyncInterrupt() // from a much safer location in code. if (m_active) { char ch = 'i'; // Send 'i' for interrupt size_t bytes_written = 0; Status result = m_pipe.Write(&ch, 1, bytes_written); return result.Success(); } else { // This IOHandler might be pushed on the stack, but not being run // currently // so do the right thing if we aren't actively watching for STDIN by // sending // the interrupt to the process. Otherwise the write to the pipe above // would // do nothing. This can happen when the command interpreter is running and // gets a "expression ...". It will be on the IOHandler thread and sending // the input is complete to the delegate which will cause the expression // to // run, which will push the process IO handler, but not run it. if (StateIsRunningState(m_process->GetState())) { m_process->SendAsyncInterrupt(); return true; } } return false; } void GotEOF() override {} protected: Process *m_process; File m_read_file; // Read from this file (usually actual STDIN for LLDB File m_write_file; // Write to this file (usually the master pty for getting // io to debuggee) Pipe m_pipe; std::atomic m_is_running{false}; }; void Process::SetSTDIOFileDescriptor(int fd) { // First set up the Read Thread for reading/handling process I/O std::unique_ptr conn_ap( new ConnectionFileDescriptor(fd, true)); if (conn_ap) { m_stdio_communication.SetConnection(conn_ap.release()); if (m_stdio_communication.IsConnected()) { m_stdio_communication.SetReadThreadBytesReceivedCallback( STDIOReadThreadBytesReceived, this); m_stdio_communication.StartReadThread(); // Now read thread is set up, set up input reader. if (!m_process_input_reader) m_process_input_reader.reset(new IOHandlerProcessSTDIO(this, fd)); } } } bool Process::ProcessIOHandlerIsActive() { IOHandlerSP io_handler_sp(m_process_input_reader); if (io_handler_sp) return GetTarget().GetDebugger().IsTopIOHandler(io_handler_sp); return false; } bool Process::PushProcessIOHandler() { IOHandlerSP io_handler_sp(m_process_input_reader); if (io_handler_sp) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::%s pushing IO handler", __FUNCTION__); io_handler_sp->SetIsDone(false); GetTarget().GetDebugger().PushIOHandler(io_handler_sp); return true; } return false; } bool Process::PopProcessIOHandler() { IOHandlerSP io_handler_sp(m_process_input_reader); if (io_handler_sp) return GetTarget().GetDebugger().PopIOHandler(io_handler_sp); return false; } // The process needs to know about installed plug-ins void Process::SettingsInitialize() { Thread::SettingsInitialize(); } void Process::SettingsTerminate() { Thread::SettingsTerminate(); } namespace { // RestorePlanState is used to record the "is private", "is master" and "okay to // discard" fields of // the plan we are running, and reset it on Clean or on destruction. // It will only reset the state once, so you can call Clean and then monkey with // the state and it // won't get reset on you again. class RestorePlanState { public: RestorePlanState(lldb::ThreadPlanSP thread_plan_sp) : m_thread_plan_sp(thread_plan_sp), m_already_reset(false) { if (m_thread_plan_sp) { m_private = m_thread_plan_sp->GetPrivate(); m_is_master = m_thread_plan_sp->IsMasterPlan(); m_okay_to_discard = m_thread_plan_sp->OkayToDiscard(); } } ~RestorePlanState() { Clean(); } void Clean() { if (!m_already_reset && m_thread_plan_sp) { m_already_reset = true; m_thread_plan_sp->SetPrivate(m_private); m_thread_plan_sp->SetIsMasterPlan(m_is_master); m_thread_plan_sp->SetOkayToDiscard(m_okay_to_discard); } } private: lldb::ThreadPlanSP m_thread_plan_sp; bool m_already_reset; bool m_private; bool m_is_master; bool m_okay_to_discard; }; } // anonymous namespace static microseconds GetOneThreadExpressionTimeout(const EvaluateExpressionOptions &options) { const milliseconds default_one_thread_timeout(250); // If the overall wait is forever, then we don't need to worry about it. if (!options.GetTimeout()) { return options.GetOneThreadTimeout() ? *options.GetOneThreadTimeout() : default_one_thread_timeout; } // If the one thread timeout is set, use it. if (options.GetOneThreadTimeout()) return *options.GetOneThreadTimeout(); // Otherwise use half the total timeout, bounded by the // default_one_thread_timeout. return std::min(default_one_thread_timeout, *options.GetTimeout() / 2); } static Timeout GetExpressionTimeout(const EvaluateExpressionOptions &options, bool before_first_timeout) { // If we are going to run all threads the whole time, or if we are only // going to run one thread, we can just return the overall timeout. if (!options.GetStopOthers() || !options.GetTryAllThreads()) return options.GetTimeout(); if (before_first_timeout) return GetOneThreadExpressionTimeout(options); if (!options.GetTimeout()) return llvm::None; else return *options.GetTimeout() - GetOneThreadExpressionTimeout(options); } +static llvm::Optional +HandleStoppedEvent(Thread &thread, const ThreadPlanSP &thread_plan_sp, + RestorePlanState &restorer, const EventSP &event_sp, + EventSP &event_to_broadcast_sp, + const EvaluateExpressionOptions &options, bool handle_interrupts) { + Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS); + + ThreadPlanSP plan = thread.GetCompletedPlan(); + if (plan == thread_plan_sp && plan->PlanSucceeded()) { + LLDB_LOG(log, "execution completed successfully"); + + // Restore the plan state so it will get reported as intended when we are + // done. + restorer.Clean(); + return eExpressionCompleted; + } + + StopInfoSP stop_info_sp = thread.GetStopInfo(); + if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonBreakpoint && + stop_info_sp->ShouldNotify(event_sp.get())) { + LLDB_LOG(log, "stopped for breakpoint: {0}.", stop_info_sp->GetDescription()); + if (!options.DoesIgnoreBreakpoints()) { + // Restore the plan state and then force Private to false. We are going + // to stop because of this plan so we need it to become a public plan or + // it won't report correctly when we continue to its termination later on. + restorer.Clean(); + thread_plan_sp->SetPrivate(false); + event_to_broadcast_sp = event_sp; + } + return eExpressionHitBreakpoint; + } + + if (!handle_interrupts && + Process::ProcessEventData::GetInterruptedFromEvent(event_sp.get())) + return llvm::None; + + LLDB_LOG(log, "thread plan did not successfully complete"); + if (!options.DoesUnwindOnError()) + event_to_broadcast_sp = event_sp; + return eExpressionInterrupted; +} + ExpressionResults Process::RunThreadPlan(ExecutionContext &exe_ctx, lldb::ThreadPlanSP &thread_plan_sp, const EvaluateExpressionOptions &options, DiagnosticManager &diagnostic_manager) { ExpressionResults return_value = eExpressionSetupError; std::lock_guard run_thread_plan_locker(m_run_thread_plan_lock); if (!thread_plan_sp) { diagnostic_manager.PutString( eDiagnosticSeverityError, "RunThreadPlan called with empty thread plan."); return eExpressionSetupError; } if (!thread_plan_sp->ValidatePlan(nullptr)) { diagnostic_manager.PutString( eDiagnosticSeverityError, "RunThreadPlan called with an invalid thread plan."); return eExpressionSetupError; } if (exe_ctx.GetProcessPtr() != this) { diagnostic_manager.PutString(eDiagnosticSeverityError, "RunThreadPlan called on wrong process."); return eExpressionSetupError; } Thread *thread = exe_ctx.GetThreadPtr(); if (thread == nullptr) { diagnostic_manager.PutString(eDiagnosticSeverityError, "RunThreadPlan called with invalid thread."); return eExpressionSetupError; } // We need to change some of the thread plan attributes for the thread plan // runner. This will restore them // when we are done: RestorePlanState thread_plan_restorer(thread_plan_sp); // We rely on the thread plan we are running returning "PlanCompleted" if when // it successfully completes. // For that to be true the plan can't be private - since private plans // suppress themselves in the // GetCompletedPlan call. thread_plan_sp->SetPrivate(false); // The plans run with RunThreadPlan also need to be terminal master plans or // when they are done we will end // up asking the plan above us whether we should stop, which may give the // wrong answer. thread_plan_sp->SetIsMasterPlan(true); thread_plan_sp->SetOkayToDiscard(false); if (m_private_state.GetValue() != eStateStopped) { diagnostic_manager.PutString( eDiagnosticSeverityError, "RunThreadPlan called while the private state was not stopped."); return eExpressionSetupError; } // Save the thread & frame from the exe_ctx for restoration after we run const uint32_t thread_idx_id = thread->GetIndexID(); StackFrameSP selected_frame_sp = thread->GetSelectedFrame(); if (!selected_frame_sp) { thread->SetSelectedFrame(nullptr); selected_frame_sp = thread->GetSelectedFrame(); if (!selected_frame_sp) { diagnostic_manager.Printf( eDiagnosticSeverityError, "RunThreadPlan called without a selected frame on thread %d", thread_idx_id); return eExpressionSetupError; } } // Make sure the timeout values make sense. The one thread timeout needs to be // smaller than the overall timeout. if (options.GetOneThreadTimeout() && options.GetTimeout() && *options.GetTimeout() < *options.GetOneThreadTimeout()) { diagnostic_manager.PutString(eDiagnosticSeverityError, "RunThreadPlan called with one thread " "timeout greater than total timeout"); return eExpressionSetupError; } StackID ctx_frame_id = selected_frame_sp->GetStackID(); // N.B. Running the target may unset the currently selected thread and frame. // We don't want to do that either, // so we should arrange to reset them as well. lldb::ThreadSP selected_thread_sp = GetThreadList().GetSelectedThread(); uint32_t selected_tid; StackID selected_stack_id; if (selected_thread_sp) { selected_tid = selected_thread_sp->GetIndexID(); selected_stack_id = selected_thread_sp->GetSelectedFrame()->GetStackID(); } else { selected_tid = LLDB_INVALID_THREAD_ID; } HostThread backup_private_state_thread; lldb::StateType old_state = eStateInvalid; lldb::ThreadPlanSP stopper_base_plan_sp; Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS)); if (m_private_state_thread.EqualsThread(Host::GetCurrentThread())) { // Yikes, we are running on the private state thread! So we can't wait for // public events on this thread, since // we are the thread that is generating public events. // The simplest thing to do is to spin up a temporary thread to handle // private state thread events while // we are fielding public events here. if (log) log->Printf("Running thread plan on private state thread, spinning up " "another state thread to handle the events."); backup_private_state_thread = m_private_state_thread; // One other bit of business: we want to run just this thread plan and // anything it pushes, and then stop, // returning control here. // But in the normal course of things, the plan above us on the stack would // be given a shot at the stop // event before deciding to stop, and we don't want that. So we insert a // "stopper" base plan on the stack // before the plan we want to run. Since base plans always stop and return // control to the user, that will // do just what we want. stopper_base_plan_sp.reset(new ThreadPlanBase(*thread)); thread->QueueThreadPlan(stopper_base_plan_sp, false); // Have to make sure our public state is stopped, since otherwise the // reporting logic below doesn't work correctly. old_state = m_public_state.GetValue(); m_public_state.SetValueNoLock(eStateStopped); // Now spin up the private state thread: StartPrivateStateThread(true); } thread->QueueThreadPlan( thread_plan_sp, false); // This used to pass "true" does that make sense? if (options.GetDebug()) { // In this case, we aren't actually going to run, we just want to stop right // away. // Flush this thread so we will refetch the stacks and show the correct // backtrace. // FIXME: To make this prettier we should invent some stop reason for this, // but that // is only cosmetic, and this functionality is only of use to lldb // developers who can // live with not pretty... thread->Flush(); return eExpressionStoppedForDebug; } ListenerSP listener_sp( Listener::MakeListener("lldb.process.listener.run-thread-plan")); lldb::EventSP event_to_broadcast_sp; { // This process event hijacker Hijacks the Public events and its destructor // makes sure that the process events get // restored on exit to the function. // // If the event needs to propagate beyond the hijacker (e.g., the process // exits during execution), then the event // is put into event_to_broadcast_sp for rebroadcasting. ProcessEventHijacker run_thread_plan_hijacker(*this, listener_sp); if (log) { StreamString s; thread_plan_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); log->Printf("Process::RunThreadPlan(): Resuming thread %u - 0x%4.4" PRIx64 " to run thread plan \"%s\".", thread->GetIndexID(), thread->GetID(), s.GetData()); } bool got_event; lldb::EventSP event_sp; lldb::StateType stop_state = lldb::eStateInvalid; bool before_first_timeout = true; // This is set to false the first time // that we have to halt the target. bool do_resume = true; bool handle_running_event = true; // This is just for accounting: uint32_t num_resumes = 0; // If we are going to run all threads the whole time, or if we are only // going to run one thread, then we don't need the first timeout. So we // pretend we are after the first timeout already. if (!options.GetStopOthers() || !options.GetTryAllThreads()) before_first_timeout = false; if (log) log->Printf("Stop others: %u, try all: %u, before_first: %u.\n", options.GetStopOthers(), options.GetTryAllThreads(), before_first_timeout); // This isn't going to work if there are unfetched events on the queue. // Are there cases where we might want to run the remaining events here, and // then try to // call the function? That's probably being too tricky for our own good. Event *other_events = listener_sp->PeekAtNextEvent(); if (other_events != nullptr) { diagnostic_manager.PutString( eDiagnosticSeverityError, "RunThreadPlan called with pending events on the queue."); return eExpressionSetupError; } // We also need to make sure that the next event is delivered. We might be // calling a function as part of // a thread plan, in which case the last delivered event could be the // running event, and we don't want // event coalescing to cause us to lose OUR running event... ForceNextEventDelivery(); // This while loop must exit out the bottom, there's cleanup that we need to do // when we are done. // So don't call return anywhere within it. #ifdef LLDB_RUN_THREAD_HALT_WITH_EVENT // It's pretty much impossible to write test cases for things like: // One thread timeout expires, I go to halt, but the process already stopped // on the function call stop breakpoint. Turning on this define will make // us not // fetch the first event till after the halt. So if you run a quick // function, it will have // completed, and the completion event will be waiting, when you interrupt // for halt. // The expression evaluation should still succeed. bool miss_first_event = true; #endif while (true) { // We usually want to resume the process if we get to the top of the loop. // The only exception is if we get two running events with no intervening // stop, which can happen, we will just wait for then next stop event. if (log) log->Printf("Top of while loop: do_resume: %i handle_running_event: %i " "before_first_timeout: %i.", do_resume, handle_running_event, before_first_timeout); if (do_resume || handle_running_event) { // Do the initial resume and wait for the running event before going // further. if (do_resume) { num_resumes++; Status resume_error = PrivateResume(); if (!resume_error.Success()) { diagnostic_manager.Printf( eDiagnosticSeverityError, "couldn't resume inferior the %d time: \"%s\".", num_resumes, resume_error.AsCString()); return_value = eExpressionSetupError; break; } } got_event = listener_sp->GetEvent(event_sp, std::chrono::milliseconds(500)); if (!got_event) { if (log) log->Printf("Process::RunThreadPlan(): didn't get any event after " "resume %" PRIu32 ", exiting.", num_resumes); diagnostic_manager.Printf(eDiagnosticSeverityError, "didn't get any event after resume %" PRIu32 ", exiting.", num_resumes); return_value = eExpressionSetupError; break; } stop_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); if (stop_state != eStateRunning) { bool restarted = false; if (stop_state == eStateStopped) { restarted = Process::ProcessEventData::GetRestartedFromEvent( event_sp.get()); if (log) log->Printf( "Process::RunThreadPlan(): didn't get running event after " "resume %d, got %s instead (restarted: %i, do_resume: %i, " "handle_running_event: %i).", num_resumes, StateAsCString(stop_state), restarted, do_resume, handle_running_event); } if (restarted) { // This is probably an overabundance of caution, I don't think I // should ever get a stopped & restarted // event here. But if I do, the best thing is to Halt and then get // out of here. const bool clear_thread_plans = false; const bool use_run_lock = false; Halt(clear_thread_plans, use_run_lock); } diagnostic_manager.Printf( eDiagnosticSeverityError, "didn't get running event after initial resume, got %s instead.", StateAsCString(stop_state)); return_value = eExpressionSetupError; break; } if (log) log->PutCString("Process::RunThreadPlan(): resuming succeeded."); // We need to call the function synchronously, so spin waiting for it to // return. // If we get interrupted while executing, we're going to lose our // context, and // won't be able to gather the result at this point. // We set the timeout AFTER the resume, since the resume takes some time // and we // don't want to charge that to the timeout. } else { if (log) log->PutCString("Process::RunThreadPlan(): waiting for next event."); } do_resume = true; handle_running_event = true; // Now wait for the process to stop again: event_sp.reset(); Timeout timeout = GetExpressionTimeout(options, before_first_timeout); if (log) { if (timeout) { auto now = system_clock::now(); log->Printf("Process::RunThreadPlan(): about to wait - now is %s - " "endpoint is %s", llvm::to_string(now).c_str(), llvm::to_string(now + *timeout).c_str()); } else { log->Printf("Process::RunThreadPlan(): about to wait forever."); } } #ifdef LLDB_RUN_THREAD_HALT_WITH_EVENT // See comment above... if (miss_first_event) { usleep(1000); miss_first_event = false; got_event = false; } else #endif got_event = listener_sp->GetEvent(event_sp, timeout); if (got_event) { if (event_sp) { bool keep_going = false; if (event_sp->GetType() == eBroadcastBitInterrupt) { const bool clear_thread_plans = false; const bool use_run_lock = false; Halt(clear_thread_plans, use_run_lock); return_value = eExpressionInterrupted; diagnostic_manager.PutString(eDiagnosticSeverityRemark, "execution halted by user interrupt."); if (log) log->Printf("Process::RunThreadPlan(): Got interrupted by " "eBroadcastBitInterrupted, exiting."); break; } else { stop_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); if (log) log->Printf( "Process::RunThreadPlan(): in while loop, got event: %s.", StateAsCString(stop_state)); switch (stop_state) { case lldb::eStateStopped: { // We stopped, figure out what we are going to do now. ThreadSP thread_sp = GetThreadList().FindThreadByIndexID(thread_idx_id); if (!thread_sp) { // Ooh, our thread has vanished. Unlikely that this was // successful execution... if (log) log->Printf("Process::RunThreadPlan(): execution completed " "but our thread (index-id=%u) has vanished.", thread_idx_id); return_value = eExpressionInterrupted; - } else { + } else if (Process::ProcessEventData::GetRestartedFromEvent( + event_sp.get())) { // If we were restarted, we just need to go back up to fetch // another event. - if (Process::ProcessEventData::GetRestartedFromEvent( - event_sp.get())) { - if (log) { - log->Printf("Process::RunThreadPlan(): Got a stop and " - "restart, so we'll continue waiting."); - } - keep_going = true; - do_resume = false; - handle_running_event = true; - } else { - ThreadPlanSP plan = thread->GetCompletedPlan(); - if (plan == thread_plan_sp && plan->PlanSucceeded()) { - - if (log) - log->PutCString("Process::RunThreadPlan(): execution " - "completed successfully."); - - // Restore the plan state so it will get reported as - // intended when we are done. - thread_plan_restorer.Clean(); - - return_value = eExpressionCompleted; - } else { - StopInfoSP stop_info_sp = thread_sp->GetStopInfo(); - // Something restarted the target, so just wait for it to - // stop for real. - if (stop_info_sp && - stop_info_sp->GetStopReason() == eStopReasonBreakpoint) { - if (log) - log->Printf("Process::RunThreadPlan() stopped for " - "breakpoint: %s.", - stop_info_sp->GetDescription()); - return_value = eExpressionHitBreakpoint; - if (!options.DoesIgnoreBreakpoints()) { - // Restore the plan state and then force Private to - // false. We are - // going to stop because of this plan so we need it to - // become a public - // plan or it won't report correctly when we continue to - // its termination - // later on. - thread_plan_restorer.Clean(); - if (thread_plan_sp) - thread_plan_sp->SetPrivate(false); - event_to_broadcast_sp = event_sp; - } - } else { - if (log) - log->PutCString("Process::RunThreadPlan(): thread plan " - "didn't successfully complete."); - if (!options.DoesUnwindOnError()) - event_to_broadcast_sp = event_sp; - return_value = eExpressionInterrupted; - } - } + if (log) { + log->Printf("Process::RunThreadPlan(): Got a stop and " + "restart, so we'll continue waiting."); } + keep_going = true; + do_resume = false; + handle_running_event = true; + } else { + const bool handle_interrupts = true; + return_value = *HandleStoppedEvent( + *thread, thread_plan_sp, thread_plan_restorer, event_sp, + event_to_broadcast_sp, options, handle_interrupts); } } break; case lldb::eStateRunning: // This shouldn't really happen, but sometimes we do get two // running events without an // intervening stop, and in that case we should just go back to // waiting for the stop. do_resume = false; keep_going = true; handle_running_event = false; break; default: if (log) log->Printf("Process::RunThreadPlan(): execution stopped with " "unexpected state: %s.", StateAsCString(stop_state)); if (stop_state == eStateExited) event_to_broadcast_sp = event_sp; diagnostic_manager.PutString( eDiagnosticSeverityError, "execution stopped with unexpected state."); return_value = eExpressionInterrupted; break; } } if (keep_going) continue; else break; } else { if (log) log->PutCString("Process::RunThreadPlan(): got_event was true, but " "the event pointer was null. How odd..."); return_value = eExpressionInterrupted; break; } } else { // If we didn't get an event that means we've timed out... // We will interrupt the process here. Depending on what we were asked // to do we will // either exit, or try with all threads running for the same timeout. if (log) { if (options.GetTryAllThreads()) { if (before_first_timeout) { LLDB_LOG(log, "Running function with one thread timeout timed out."); } else LLDB_LOG(log, "Restarting function with all threads enabled and " "timeout: {0} timed out, abandoning execution.", timeout); } else LLDB_LOG(log, "Running function with timeout: {0} timed out, " "abandoning execution.", timeout); } // It is possible that between the time we issued the Halt, and we get // around to calling Halt the target // could have stopped. That's fine, Halt will figure that out and send // the appropriate Stopped event. // BUT it is also possible that we stopped & restarted (e.g. hit a // signal with "stop" set to false.) In // that case, we'll get the stopped & restarted event, and we should go // back to waiting for the Halt's // stopped event. That's what this while loop does. bool back_to_top = true; uint32_t try_halt_again = 0; bool do_halt = true; const uint32_t num_retries = 5; while (try_halt_again < num_retries) { Status halt_error; if (do_halt) { if (log) log->Printf("Process::RunThreadPlan(): Running Halt."); const bool clear_thread_plans = false; const bool use_run_lock = false; Halt(clear_thread_plans, use_run_lock); } if (halt_error.Success()) { if (log) log->PutCString("Process::RunThreadPlan(): Halt succeeded."); got_event = listener_sp->GetEvent(event_sp, std::chrono::milliseconds(500)); if (got_event) { stop_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); if (log) { log->Printf("Process::RunThreadPlan(): Stopped with event: %s", StateAsCString(stop_state)); if (stop_state == lldb::eStateStopped && Process::ProcessEventData::GetInterruptedFromEvent( event_sp.get())) log->PutCString(" Event was the Halt interruption event."); } if (stop_state == lldb::eStateStopped) { - // Between the time we initiated the Halt and the time we - // delivered it, the process could have - // already finished its job. Check that here: - - if (thread->IsThreadPlanDone(thread_plan_sp.get())) { - if (log) - log->PutCString("Process::RunThreadPlan(): Even though we " - "timed out, the call plan was done. " - "Exiting wait loop."); - return_value = eExpressionCompleted; - back_to_top = false; - break; - } - if (Process::ProcessEventData::GetRestartedFromEvent( event_sp.get())) { if (log) log->PutCString("Process::RunThreadPlan(): Went to halt " "but got a restarted event, there must be " "an un-restarted stopped event so try " "again... " "Exiting wait loop."); try_halt_again++; do_halt = false; continue; + } + + // Between the time we initiated the Halt and the time we + // delivered it, the process could have + // already finished its job. Check that here: + const bool handle_interrupts = false; + if (auto result = HandleStoppedEvent( + *thread, thread_plan_sp, thread_plan_restorer, event_sp, + event_to_broadcast_sp, options, handle_interrupts)) { + return_value = *result; + back_to_top = false; + break; } if (!options.GetTryAllThreads()) { if (log) log->PutCString("Process::RunThreadPlan(): try_all_threads " "was false, we stopped so now we're " "quitting."); return_value = eExpressionInterrupted; back_to_top = false; break; } if (before_first_timeout) { // Set all the other threads to run, and return to the top of // the loop, which will continue; before_first_timeout = false; thread_plan_sp->SetStopOthers(false); if (log) log->PutCString( "Process::RunThreadPlan(): about to resume."); back_to_top = true; break; } else { // Running all threads failed, so return Interrupted. if (log) log->PutCString("Process::RunThreadPlan(): running all " "threads timed out."); return_value = eExpressionInterrupted; back_to_top = false; break; } } } else { if (log) log->PutCString("Process::RunThreadPlan(): halt said it " "succeeded, but I got no event. " "I'm getting out of here passing Interrupted."); return_value = eExpressionInterrupted; back_to_top = false; break; } } else { try_halt_again++; continue; } } if (!back_to_top || try_halt_again > num_retries) break; else continue; } } // END WAIT LOOP // If we had to start up a temporary private state thread to run this thread // plan, shut it down now. if (backup_private_state_thread.IsJoinable()) { StopPrivateStateThread(); Status error; m_private_state_thread = backup_private_state_thread; if (stopper_base_plan_sp) { thread->DiscardThreadPlansUpToPlan(stopper_base_plan_sp); } if (old_state != eStateInvalid) m_public_state.SetValueNoLock(old_state); } if (return_value != eExpressionCompleted && log) { // Print a backtrace into the log so we can figure out where we are: StreamString s; s.PutCString("Thread state after unsuccessful completion: \n"); thread->GetStackFrameStatus(s, 0, UINT32_MAX, true, UINT32_MAX); log->PutString(s.GetString()); } // Restore the thread state if we are going to discard the plan execution. // There are three cases where this // could happen: // 1) The execution successfully completed // 2) We hit a breakpoint, and ignore_breakpoints was true // 3) We got some other error, and discard_on_error was true bool should_unwind = (return_value == eExpressionInterrupted && options.DoesUnwindOnError()) || (return_value == eExpressionHitBreakpoint && options.DoesIgnoreBreakpoints()); if (return_value == eExpressionCompleted || should_unwind) { thread_plan_sp->RestoreThreadState(); } // Now do some processing on the results of the run: if (return_value == eExpressionInterrupted || return_value == eExpressionHitBreakpoint) { if (log) { StreamString s; if (event_sp) event_sp->Dump(&s); else { log->PutCString("Process::RunThreadPlan(): Stop event that " "interrupted us is NULL."); } StreamString ts; const char *event_explanation = nullptr; do { if (!event_sp) { event_explanation = ""; break; } else if (event_sp->GetType() == eBroadcastBitInterrupt) { event_explanation = ""; break; } else { const Process::ProcessEventData *event_data = Process::ProcessEventData::GetEventDataFromEvent( event_sp.get()); if (!event_data) { event_explanation = ""; break; } Process *process = event_data->GetProcessSP().get(); if (!process) { event_explanation = ""; break; } ThreadList &thread_list = process->GetThreadList(); uint32_t num_threads = thread_list.GetSize(); uint32_t thread_index; ts.Printf("<%u threads> ", num_threads); for (thread_index = 0; thread_index < num_threads; ++thread_index) { Thread *thread = thread_list.GetThreadAtIndex(thread_index).get(); if (!thread) { ts.Printf(" "); continue; } ts.Printf("<0x%4.4" PRIx64 " ", thread->GetID()); RegisterContext *register_context = thread->GetRegisterContext().get(); if (register_context) ts.Printf("[ip 0x%" PRIx64 "] ", register_context->GetPC()); else ts.Printf("[ip unknown] "); // Show the private stop info here, the public stop info will be // from the last natural stop. lldb::StopInfoSP stop_info_sp = thread->GetPrivateStopInfo(); if (stop_info_sp) { const char *stop_desc = stop_info_sp->GetDescription(); if (stop_desc) ts.PutCString(stop_desc); } ts.Printf(">"); } event_explanation = ts.GetData(); } } while (0); if (event_explanation) log->Printf("Process::RunThreadPlan(): execution interrupted: %s %s", s.GetData(), event_explanation); else log->Printf("Process::RunThreadPlan(): execution interrupted: %s", s.GetData()); } if (should_unwind) { if (log) log->Printf("Process::RunThreadPlan: ExecutionInterrupted - " "discarding thread plans up to %p.", static_cast(thread_plan_sp.get())); thread->DiscardThreadPlansUpToPlan(thread_plan_sp); } else { if (log) log->Printf("Process::RunThreadPlan: ExecutionInterrupted - for " "plan: %p not discarding.", static_cast(thread_plan_sp.get())); } } else if (return_value == eExpressionSetupError) { if (log) log->PutCString("Process::RunThreadPlan(): execution set up error."); if (options.DoesUnwindOnError()) { thread->DiscardThreadPlansUpToPlan(thread_plan_sp); } } else { if (thread->IsThreadPlanDone(thread_plan_sp.get())) { if (log) log->PutCString("Process::RunThreadPlan(): thread plan is done"); return_value = eExpressionCompleted; } else if (thread->WasThreadPlanDiscarded(thread_plan_sp.get())) { if (log) log->PutCString( "Process::RunThreadPlan(): thread plan was discarded"); return_value = eExpressionDiscarded; } else { if (log) log->PutCString( "Process::RunThreadPlan(): thread plan stopped in mid course"); if (options.DoesUnwindOnError() && thread_plan_sp) { if (log) log->PutCString("Process::RunThreadPlan(): discarding thread plan " "'cause unwind_on_error is set."); thread->DiscardThreadPlansUpToPlan(thread_plan_sp); } } } // Thread we ran the function in may have gone away because we ran the // target // Check that it's still there, and if it is put it back in the context. // Also restore the // frame in the context if it is still present. thread = GetThreadList().FindThreadByIndexID(thread_idx_id, true).get(); if (thread) { exe_ctx.SetFrameSP(thread->GetFrameWithStackID(ctx_frame_id)); } // Also restore the current process'es selected frame & thread, since this // function calling may // be done behind the user's back. if (selected_tid != LLDB_INVALID_THREAD_ID) { if (GetThreadList().SetSelectedThreadByIndexID(selected_tid) && selected_stack_id.IsValid()) { // We were able to restore the selected thread, now restore the frame: std::lock_guard guard(GetThreadList().GetMutex()); StackFrameSP old_frame_sp = GetThreadList().GetSelectedThread()->GetFrameWithStackID( selected_stack_id); if (old_frame_sp) GetThreadList().GetSelectedThread()->SetSelectedFrame( old_frame_sp.get()); } } } // If the process exited during the run of the thread plan, notify everyone. if (event_to_broadcast_sp) { if (log) log->PutCString("Process::RunThreadPlan(): rebroadcasting event."); BroadcastEvent(event_to_broadcast_sp); } return return_value; } const char *Process::ExecutionResultAsCString(ExpressionResults result) { const char *result_name; switch (result) { case eExpressionCompleted: result_name = "eExpressionCompleted"; break; case eExpressionDiscarded: result_name = "eExpressionDiscarded"; break; case eExpressionInterrupted: result_name = "eExpressionInterrupted"; break; case eExpressionHitBreakpoint: result_name = "eExpressionHitBreakpoint"; break; case eExpressionSetupError: result_name = "eExpressionSetupError"; break; case eExpressionParseError: result_name = "eExpressionParseError"; break; case eExpressionResultUnavailable: result_name = "eExpressionResultUnavailable"; break; case eExpressionTimedOut: result_name = "eExpressionTimedOut"; break; case eExpressionStoppedForDebug: result_name = "eExpressionStoppedForDebug"; break; } return result_name; } void Process::GetStatus(Stream &strm) { const StateType state = GetState(); if (StateIsStoppedState(state, false)) { if (state == eStateExited) { int exit_status = GetExitStatus(); const char *exit_description = GetExitDescription(); strm.Printf("Process %" PRIu64 " exited with status = %i (0x%8.8x) %s\n", GetID(), exit_status, exit_status, exit_description ? exit_description : ""); } else { if (state == eStateConnected) strm.Printf("Connected to remote target.\n"); else strm.Printf("Process %" PRIu64 " %s\n", GetID(), StateAsCString(state)); } } else { strm.Printf("Process %" PRIu64 " is running.\n", GetID()); } } size_t Process::GetThreadStatus(Stream &strm, bool only_threads_with_stop_reason, uint32_t start_frame, uint32_t num_frames, uint32_t num_frames_with_source, bool stop_format) { size_t num_thread_infos_dumped = 0; // You can't hold the thread list lock while calling Thread::GetStatus. That // very well might run code (e.g. if we need it // to get return values or arguments.) For that to work the process has to be // able to acquire it. So instead copy the thread // ID's, and look them up one by one: uint32_t num_threads; std::vector thread_id_array; // Scope for thread list locker; { std::lock_guard guard(GetThreadList().GetMutex()); ThreadList &curr_thread_list = GetThreadList(); num_threads = curr_thread_list.GetSize(); uint32_t idx; thread_id_array.resize(num_threads); for (idx = 0; idx < num_threads; ++idx) thread_id_array[idx] = curr_thread_list.GetThreadAtIndex(idx)->GetID(); } for (uint32_t i = 0; i < num_threads; i++) { ThreadSP thread_sp(GetThreadList().FindThreadByID(thread_id_array[i])); if (thread_sp) { if (only_threads_with_stop_reason) { StopInfoSP stop_info_sp = thread_sp->GetStopInfo(); if (!stop_info_sp || !stop_info_sp->IsValid()) continue; } thread_sp->GetStatus(strm, start_frame, num_frames, num_frames_with_source, stop_format); ++num_thread_infos_dumped; } else { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::GetThreadStatus - thread 0x" PRIu64 " vanished while running Thread::GetStatus."); } } return num_thread_infos_dumped; } void Process::AddInvalidMemoryRegion(const LoadRange ®ion) { m_memory_cache.AddInvalidRange(region.GetRangeBase(), region.GetByteSize()); } bool Process::RemoveInvalidMemoryRange(const LoadRange ®ion) { return m_memory_cache.RemoveInvalidRange(region.GetRangeBase(), region.GetByteSize()); } void Process::AddPreResumeAction(PreResumeActionCallback callback, void *baton) { m_pre_resume_actions.push_back(PreResumeCallbackAndBaton(callback, baton)); } bool Process::RunPreResumeActions() { bool result = true; while (!m_pre_resume_actions.empty()) { struct PreResumeCallbackAndBaton action = m_pre_resume_actions.back(); m_pre_resume_actions.pop_back(); bool this_result = action.callback(action.baton); if (result) result = this_result; } return result; } void Process::ClearPreResumeActions() { m_pre_resume_actions.clear(); } void Process::ClearPreResumeAction(PreResumeActionCallback callback, void *baton) { PreResumeCallbackAndBaton element(callback, baton); auto found_iter = std::find(m_pre_resume_actions.begin(), m_pre_resume_actions.end(), element); if (found_iter != m_pre_resume_actions.end()) { m_pre_resume_actions.erase(found_iter); } } ProcessRunLock &Process::GetRunLock() { if (m_private_state_thread.EqualsThread(Host::GetCurrentThread())) return m_private_run_lock; else return m_public_run_lock; } void Process::Flush() { m_thread_list.Flush(); m_extended_thread_list.Flush(); m_extended_thread_stop_id = 0; m_queue_list.Clear(); m_queue_list_stop_id = 0; } void Process::DidExec() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::%s()", __FUNCTION__); Target &target = GetTarget(); target.CleanupProcess(); target.ClearModules(false); m_dynamic_checkers_ap.reset(); m_abi_sp.reset(); m_system_runtime_ap.reset(); m_os_ap.reset(); m_dyld_ap.reset(); m_jit_loaders_ap.reset(); m_image_tokens.clear(); m_allocated_memory_cache.Clear(); m_language_runtimes.clear(); m_instrumentation_runtimes.clear(); m_thread_list.DiscardThreadPlans(); m_memory_cache.Clear(true); m_stop_info_override_callback = nullptr; DoDidExec(); CompleteAttach(); // Flush the process (threads and all stack frames) after running // CompleteAttach() // in case the dynamic loader loaded things in new locations. Flush(); // After we figure out what was loaded/unloaded in CompleteAttach, // we need to let the target know so it can do any cleanup it needs to. target.DidExec(); } addr_t Process::ResolveIndirectFunction(const Address *address, Status &error) { if (address == nullptr) { error.SetErrorString("Invalid address argument"); return LLDB_INVALID_ADDRESS; } addr_t function_addr = LLDB_INVALID_ADDRESS; addr_t addr = address->GetLoadAddress(&GetTarget()); std::map::const_iterator iter = m_resolved_indirect_addresses.find(addr); if (iter != m_resolved_indirect_addresses.end()) { function_addr = (*iter).second; } else { if (!InferiorCall(this, address, function_addr)) { Symbol *symbol = address->CalculateSymbolContextSymbol(); error.SetErrorStringWithFormat( "Unable to call resolver for indirect function %s", symbol ? symbol->GetName().AsCString() : ""); function_addr = LLDB_INVALID_ADDRESS; } else { m_resolved_indirect_addresses.insert( std::pair(addr, function_addr)); } } return function_addr; } void Process::ModulesDidLoad(ModuleList &module_list) { SystemRuntime *sys_runtime = GetSystemRuntime(); if (sys_runtime) { sys_runtime->ModulesDidLoad(module_list); } GetJITLoaders().ModulesDidLoad(module_list); // Give runtimes a chance to be created. InstrumentationRuntime::ModulesDidLoad(module_list, this, m_instrumentation_runtimes); // Tell runtimes about new modules. for (auto pos = m_instrumentation_runtimes.begin(); pos != m_instrumentation_runtimes.end(); ++pos) { InstrumentationRuntimeSP runtime = pos->second; runtime->ModulesDidLoad(module_list); } // Let any language runtimes we have already created know // about the modules that loaded. // Iterate over a copy of this language runtime list in case // the language runtime ModulesDidLoad somehow causes the language // riuntime to be unloaded. LanguageRuntimeCollection language_runtimes(m_language_runtimes); for (const auto &pair : language_runtimes) { // We must check language_runtime_sp to make sure it is not // nullptr as we might cache the fact that we didn't have a // language runtime for a language. LanguageRuntimeSP language_runtime_sp = pair.second; if (language_runtime_sp) language_runtime_sp->ModulesDidLoad(module_list); } // If we don't have an operating system plug-in, try to load one since // loading shared libraries might cause a new one to try and load if (!m_os_ap) LoadOperatingSystemPlugin(false); // Give structured-data plugins a chance to see the modified modules. for (auto pair : m_structured_data_plugin_map) { if (pair.second) pair.second->ModulesDidLoad(*this, module_list); } } void Process::PrintWarning(uint64_t warning_type, const void *repeat_key, const char *fmt, ...) { bool print_warning = true; StreamSP stream_sp = GetTarget().GetDebugger().GetAsyncOutputStream(); if (!stream_sp) return; if (warning_type == eWarningsOptimization && !GetWarningsOptimization()) { return; } if (repeat_key != nullptr) { WarningsCollection::iterator it = m_warnings_issued.find(warning_type); if (it == m_warnings_issued.end()) { m_warnings_issued[warning_type] = WarningsPointerSet(); m_warnings_issued[warning_type].insert(repeat_key); } else { if (it->second.find(repeat_key) != it->second.end()) { print_warning = false; } else { it->second.insert(repeat_key); } } } if (print_warning) { va_list args; va_start(args, fmt); stream_sp->PrintfVarArg(fmt, args); va_end(args); } } void Process::PrintWarningOptimization(const SymbolContext &sc) { if (GetWarningsOptimization() && sc.module_sp && !sc.module_sp->GetFileSpec().GetFilename().IsEmpty() && sc.function && sc.function->GetIsOptimized()) { PrintWarning(Process::Warnings::eWarningsOptimization, sc.module_sp.get(), "%s was compiled with optimization - stepping may behave " "oddly; variables may not be available.\n", sc.module_sp->GetFileSpec().GetFilename().GetCString()); } } bool Process::GetProcessInfo(ProcessInstanceInfo &info) { info.Clear(); PlatformSP platform_sp = GetTarget().GetPlatform(); if (!platform_sp) return false; return platform_sp->GetProcessInfo(GetID(), info); } ThreadCollectionSP Process::GetHistoryThreads(lldb::addr_t addr) { ThreadCollectionSP threads; const MemoryHistorySP &memory_history = MemoryHistory::FindPlugin(shared_from_this()); if (!memory_history) { return threads; } threads.reset(new ThreadCollection(memory_history->GetHistoryThreads(addr))); return threads; } InstrumentationRuntimeSP Process::GetInstrumentationRuntime(lldb::InstrumentationRuntimeType type) { InstrumentationRuntimeCollection::iterator pos; pos = m_instrumentation_runtimes.find(type); if (pos == m_instrumentation_runtimes.end()) { return InstrumentationRuntimeSP(); } else return (*pos).second; } bool Process::GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, ModuleSpec &module_spec) { module_spec.Clear(); return false; } size_t Process::AddImageToken(lldb::addr_t image_ptr) { m_image_tokens.push_back(image_ptr); return m_image_tokens.size() - 1; } lldb::addr_t Process::GetImagePtrFromToken(size_t token) const { if (token < m_image_tokens.size()) return m_image_tokens[token]; return LLDB_INVALID_IMAGE_TOKEN; } void Process::ResetImageToken(size_t token) { if (token < m_image_tokens.size()) m_image_tokens[token] = LLDB_INVALID_IMAGE_TOKEN; } Address Process::AdvanceAddressToNextBranchInstruction(Address default_stop_addr, AddressRange range_bounds) { Target &target = GetTarget(); DisassemblerSP disassembler_sp; InstructionList *insn_list = nullptr; Address retval = default_stop_addr; if (!target.GetUseFastStepping()) return retval; if (!default_stop_addr.IsValid()) return retval; ExecutionContext exe_ctx(this); const char *plugin_name = nullptr; const char *flavor = nullptr; const bool prefer_file_cache = true; disassembler_sp = Disassembler::DisassembleRange( target.GetArchitecture(), plugin_name, flavor, exe_ctx, range_bounds, prefer_file_cache); if (disassembler_sp) insn_list = &disassembler_sp->GetInstructionList(); if (insn_list == nullptr) { return retval; } size_t insn_offset = insn_list->GetIndexOfInstructionAtAddress(default_stop_addr); if (insn_offset == UINT32_MAX) { return retval; } uint32_t branch_index = insn_list->GetIndexOfNextBranchInstruction(insn_offset, target); if (branch_index == UINT32_MAX) { return retval; } if (branch_index > insn_offset) { Address next_branch_insn_address = insn_list->GetInstructionAtIndex(branch_index)->GetAddress(); if (next_branch_insn_address.IsValid() && range_bounds.ContainsFileAddress(next_branch_insn_address)) { retval = next_branch_insn_address; } } return retval; } Status Process::GetMemoryRegions(std::vector ®ion_list) { Status error; lldb::addr_t range_end = 0; region_list.clear(); do { lldb::MemoryRegionInfoSP region_info(new lldb_private::MemoryRegionInfo()); error = GetMemoryRegionInfo(range_end, *region_info); // GetMemoryRegionInfo should only return an error if it is unimplemented. if (error.Fail()) { region_list.clear(); break; } range_end = region_info->GetRange().GetRangeEnd(); if (region_info->GetMapped() == MemoryRegionInfo::eYes) { region_list.push_back(region_info); } } while (range_end != LLDB_INVALID_ADDRESS); return error; } Status Process::ConfigureStructuredData(const ConstString &type_name, const StructuredData::ObjectSP &config_sp) { // If you get this, the Process-derived class needs to implement a method // to enable an already-reported asynchronous structured data feature. // See ProcessGDBRemote for an example implementation over gdb-remote. return Status("unimplemented"); } void Process::MapSupportedStructuredDataPlugins( const StructuredData::Array &supported_type_names) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // Bail out early if there are no type names to map. if (supported_type_names.GetSize() == 0) { if (log) log->Printf("Process::%s(): no structured data types supported", __FUNCTION__); return; } // Convert StructuredData type names to ConstString instances. std::set const_type_names; if (log) log->Printf("Process::%s(): the process supports the following async " "structured data types:", __FUNCTION__); supported_type_names.ForEach( [&const_type_names, &log](StructuredData::Object *object) { if (!object) { // Invalid - shouldn't be null objects in the array. return false; } auto type_name = object->GetAsString(); if (!type_name) { // Invalid format - all type names should be strings. return false; } const_type_names.insert(ConstString(type_name->GetValue())); LLDB_LOG(log, "- {0}", type_name->GetValue()); return true; }); // For each StructuredDataPlugin, if the plugin handles any of the // types in the supported_type_names, map that type name to that plugin. uint32_t plugin_index = 0; for (auto create_instance = PluginManager::GetStructuredDataPluginCreateCallbackAtIndex( plugin_index); create_instance && !const_type_names.empty(); ++plugin_index) { // Create the plugin. StructuredDataPluginSP plugin_sp = (*create_instance)(*this); if (!plugin_sp) { // This plugin doesn't think it can work with the process. // Move on to the next. continue; } // For any of the remaining type names, map any that this plugin // supports. std::vector names_to_remove; for (auto &type_name : const_type_names) { if (plugin_sp->SupportsStructuredDataType(type_name)) { m_structured_data_plugin_map.insert( std::make_pair(type_name, plugin_sp)); names_to_remove.push_back(type_name); if (log) log->Printf("Process::%s(): using plugin %s for type name " "%s", __FUNCTION__, plugin_sp->GetPluginName().GetCString(), type_name.GetCString()); } } // Remove the type names that were consumed by this plugin. for (auto &type_name : names_to_remove) const_type_names.erase(type_name); } } bool Process::RouteAsyncStructuredData( const StructuredData::ObjectSP object_sp) { // Nothing to do if there's no data. if (!object_sp) return false; // The contract is this must be a dictionary, so we can look up the // routing key via the top-level 'type' string value within the dictionary. StructuredData::Dictionary *dictionary = object_sp->GetAsDictionary(); if (!dictionary) return false; // Grab the async structured type name (i.e. the feature/plugin name). ConstString type_name; if (!dictionary->GetValueForKeyAsString("type", type_name)) return false; // Check if there's a plugin registered for this type name. auto find_it = m_structured_data_plugin_map.find(type_name); if (find_it == m_structured_data_plugin_map.end()) { // We don't have a mapping for this structured data type. return false; } // Route the structured data to the plugin. find_it->second->HandleArrivalOfStructuredData(*this, type_name, object_sp); return true; } Status Process::UpdateAutomaticSignalFiltering() { // Default implementation does nothign. // No automatic signal filtering to speak of. return Status(); } Index: vendor/lldb/dist/source/Target/Thread.cpp =================================================================== --- vendor/lldb/dist/source/Target/Thread.cpp (revision 319149) +++ vendor/lldb/dist/source/Target/Thread.cpp (revision 319150) @@ -1,2210 +1,2208 @@ //===-- Thread.cpp ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Target/Thread.h" #include "Plugins/Process/Utility/UnwindLLDB.h" #include "Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/FormatEntity.h" #include "lldb/Core/Module.h" #include "lldb/Core/State.h" #include "lldb/Core/ValueObject.h" #include "lldb/Host/Host.h" #include "lldb/Interpreter/OptionValueFileSpecList.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/Property.h" #include "lldb/Symbol/Function.h" #include "lldb/Target/ABI.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanBase.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanPython.h" #include "lldb/Target/ThreadPlanRunToAddress.h" #include "lldb/Target/ThreadPlanStepInRange.h" #include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Target/ThreadPlanStepOut.h" #include "lldb/Target/ThreadPlanStepOverBreakpoint.h" #include "lldb/Target/ThreadPlanStepOverRange.h" #include "lldb/Target/ThreadPlanStepThrough.h" #include "lldb/Target/ThreadPlanStepUntil.h" #include "lldb/Target/ThreadSpec.h" #include "lldb/Target/Unwind.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StreamString.h" +#include "lldb/lldb-enumerations.h" using namespace lldb; using namespace lldb_private; const ThreadPropertiesSP &Thread::GetGlobalProperties() { // NOTE: intentional leak so we don't crash if global destructor chain gets // called as other threads still use the result of this function static ThreadPropertiesSP *g_settings_sp_ptr = new ThreadPropertiesSP(new ThreadProperties(true)); return *g_settings_sp_ptr; } static PropertyDefinition g_properties[] = { {"step-in-avoid-nodebug", OptionValue::eTypeBoolean, true, true, nullptr, nullptr, "If true, step-in will not stop in functions with no debug information."}, {"step-out-avoid-nodebug", OptionValue::eTypeBoolean, true, false, nullptr, nullptr, "If true, when step-in/step-out/step-over leave the current " "frame, they will continue to step out till they come to a " "function with " "debug information. Passing a frame argument to step-out will " "override this option."}, {"step-avoid-regexp", OptionValue::eTypeRegex, true, 0, "^std::", nullptr, "A regular expression defining functions step-in won't stop in."}, {"step-avoid-libraries", OptionValue::eTypeFileSpecList, true, 0, nullptr, nullptr, "A list of libraries that source stepping won't stop in."}, {"trace-thread", OptionValue::eTypeBoolean, false, false, nullptr, nullptr, "If true, this thread will single-step and log execution."}, {nullptr, OptionValue::eTypeInvalid, false, 0, nullptr, nullptr, nullptr}}; enum { ePropertyStepInAvoidsNoDebug, ePropertyStepOutAvoidsNoDebug, ePropertyStepAvoidRegex, ePropertyStepAvoidLibraries, ePropertyEnableThreadTrace }; class ThreadOptionValueProperties : public OptionValueProperties { public: ThreadOptionValueProperties(const ConstString &name) : OptionValueProperties(name) {} // This constructor is used when creating ThreadOptionValueProperties when it // is part of a new lldb_private::Thread instance. It will copy all current // global property values as needed ThreadOptionValueProperties(ThreadProperties *global_properties) : OptionValueProperties(*global_properties->GetValueProperties()) {} const Property *GetPropertyAtIndex(const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const override { // When getting the value for a key from the thread options, we will always // try and grab the setting from the current thread if there is one. Else we // just // use the one from this instance. if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { ThreadOptionValueProperties *instance_properties = static_cast( thread->GetValueProperties().get()); if (this != instance_properties) return instance_properties->ProtectedGetPropertyAtIndex(idx); } } return ProtectedGetPropertyAtIndex(idx); } }; ThreadProperties::ThreadProperties(bool is_global) : Properties() { if (is_global) { m_collection_sp.reset( new ThreadOptionValueProperties(ConstString("thread"))); m_collection_sp->Initialize(g_properties); } else m_collection_sp.reset( new ThreadOptionValueProperties(Thread::GetGlobalProperties().get())); } ThreadProperties::~ThreadProperties() = default; const RegularExpression *ThreadProperties::GetSymbolsToAvoidRegexp() { const uint32_t idx = ePropertyStepAvoidRegex; return m_collection_sp->GetPropertyAtIndexAsOptionValueRegex(nullptr, idx); } FileSpecList &ThreadProperties::GetLibrariesToAvoid() const { const uint32_t idx = ePropertyStepAvoidLibraries; OptionValueFileSpecList *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, false, idx); assert(option_value); return option_value->GetCurrentValue(); } bool ThreadProperties::GetTraceEnabledState() const { const uint32_t idx = ePropertyEnableThreadTrace; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool ThreadProperties::GetStepInAvoidsNoDebug() const { const uint32_t idx = ePropertyStepInAvoidsNoDebug; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool ThreadProperties::GetStepOutAvoidsNoDebug() const { const uint32_t idx = ePropertyStepOutAvoidsNoDebug; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } //------------------------------------------------------------------ // Thread Event Data //------------------------------------------------------------------ const ConstString &Thread::ThreadEventData::GetFlavorString() { static ConstString g_flavor("Thread::ThreadEventData"); return g_flavor; } Thread::ThreadEventData::ThreadEventData(const lldb::ThreadSP thread_sp) : m_thread_sp(thread_sp), m_stack_id() {} Thread::ThreadEventData::ThreadEventData(const lldb::ThreadSP thread_sp, const StackID &stack_id) : m_thread_sp(thread_sp), m_stack_id(stack_id) {} Thread::ThreadEventData::ThreadEventData() : m_thread_sp(), m_stack_id() {} Thread::ThreadEventData::~ThreadEventData() = default; void Thread::ThreadEventData::Dump(Stream *s) const {} const Thread::ThreadEventData * Thread::ThreadEventData::GetEventDataFromEvent(const Event *event_ptr) { if (event_ptr) { const EventData *event_data = event_ptr->GetData(); if (event_data && event_data->GetFlavor() == ThreadEventData::GetFlavorString()) return static_cast(event_ptr->GetData()); } return nullptr; } ThreadSP Thread::ThreadEventData::GetThreadFromEvent(const Event *event_ptr) { ThreadSP thread_sp; const ThreadEventData *event_data = GetEventDataFromEvent(event_ptr); if (event_data) thread_sp = event_data->GetThread(); return thread_sp; } StackID Thread::ThreadEventData::GetStackIDFromEvent(const Event *event_ptr) { StackID stack_id; const ThreadEventData *event_data = GetEventDataFromEvent(event_ptr); if (event_data) stack_id = event_data->GetStackID(); return stack_id; } StackFrameSP Thread::ThreadEventData::GetStackFrameFromEvent(const Event *event_ptr) { const ThreadEventData *event_data = GetEventDataFromEvent(event_ptr); StackFrameSP frame_sp; if (event_data) { ThreadSP thread_sp = event_data->GetThread(); if (thread_sp) { frame_sp = thread_sp->GetStackFrameList()->GetFrameWithStackID( event_data->GetStackID()); } } return frame_sp; } //------------------------------------------------------------------ // Thread class //------------------------------------------------------------------ ConstString &Thread::GetStaticBroadcasterClass() { static ConstString class_name("lldb.thread"); return class_name; } Thread::Thread(Process &process, lldb::tid_t tid, bool use_invalid_index_id) : ThreadProperties(false), UserID(tid), Broadcaster(process.GetTarget().GetDebugger().GetBroadcasterManager(), Thread::GetStaticBroadcasterClass().AsCString()), m_process_wp(process.shared_from_this()), m_stop_info_sp(), m_stop_info_stop_id(0), m_stop_info_override_stop_id(0), m_index_id(use_invalid_index_id ? LLDB_INVALID_INDEX32 : process.GetNextThreadIndexID(tid)), m_reg_context_sp(), m_state(eStateUnloaded), m_state_mutex(), m_plan_stack(), m_completed_plan_stack(), m_frame_mutex(), m_curr_frames_sp(), m_prev_frames_sp(), m_resume_signal(LLDB_INVALID_SIGNAL_NUMBER), m_resume_state(eStateRunning), m_temporary_resume_state(eStateRunning), m_unwinder_ap(), m_destroy_called(false), m_override_should_notify(eLazyBoolCalculate), m_extended_info_fetched(false), m_extended_info() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); if (log) log->Printf("%p Thread::Thread(tid = 0x%4.4" PRIx64 ")", static_cast(this), GetID()); CheckInWithManager(); QueueFundamentalPlan(true); } Thread::~Thread() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); if (log) log->Printf("%p Thread::~Thread(tid = 0x%4.4" PRIx64 ")", static_cast(this), GetID()); /// If you hit this assert, it means your derived class forgot to call /// DoDestroy in its destructor. assert(m_destroy_called); } void Thread::DestroyThread() { // Tell any plans on the plan stacks that the thread is being destroyed since // any plans that have a thread go away in the middle of might need // to do cleanup, or in some cases NOT do cleanup... for (auto plan : m_plan_stack) plan->ThreadDestroyed(); for (auto plan : m_discarded_plan_stack) plan->ThreadDestroyed(); for (auto plan : m_completed_plan_stack) plan->ThreadDestroyed(); m_destroy_called = true; m_plan_stack.clear(); m_discarded_plan_stack.clear(); m_completed_plan_stack.clear(); // Push a ThreadPlanNull on the plan stack. That way we can continue assuming // that the // plan stack is never empty, but if somebody errantly asks questions of a // destroyed thread // without checking first whether it is destroyed, they won't crash. ThreadPlanSP null_plan_sp(new ThreadPlanNull(*this)); m_plan_stack.push_back(null_plan_sp); m_stop_info_sp.reset(); m_reg_context_sp.reset(); m_unwinder_ap.reset(); std::lock_guard guard(m_frame_mutex); m_curr_frames_sp.reset(); m_prev_frames_sp.reset(); } void Thread::BroadcastSelectedFrameChange(StackID &new_frame_id) { if (EventTypeHasListeners(eBroadcastBitSelectedFrameChanged)) BroadcastEvent(eBroadcastBitSelectedFrameChanged, new ThreadEventData(this->shared_from_this(), new_frame_id)); } lldb::StackFrameSP Thread::GetSelectedFrame() { StackFrameListSP stack_frame_list_sp(GetStackFrameList()); StackFrameSP frame_sp = stack_frame_list_sp->GetFrameAtIndex( stack_frame_list_sp->GetSelectedFrameIndex()); FunctionOptimizationWarning(frame_sp.get()); return frame_sp; } uint32_t Thread::SetSelectedFrame(lldb_private::StackFrame *frame, bool broadcast) { uint32_t ret_value = GetStackFrameList()->SetSelectedFrame(frame); if (broadcast) BroadcastSelectedFrameChange(frame->GetStackID()); FunctionOptimizationWarning(frame); return ret_value; } bool Thread::SetSelectedFrameByIndex(uint32_t frame_idx, bool broadcast) { StackFrameSP frame_sp(GetStackFrameList()->GetFrameAtIndex(frame_idx)); if (frame_sp) { GetStackFrameList()->SetSelectedFrame(frame_sp.get()); if (broadcast) BroadcastSelectedFrameChange(frame_sp->GetStackID()); FunctionOptimizationWarning(frame_sp.get()); return true; } else return false; } bool Thread::SetSelectedFrameByIndexNoisily(uint32_t frame_idx, Stream &output_stream) { const bool broadcast = true; bool success = SetSelectedFrameByIndex(frame_idx, broadcast); if (success) { StackFrameSP frame_sp = GetSelectedFrame(); if (frame_sp) { bool already_shown = false; SymbolContext frame_sc( frame_sp->GetSymbolContext(eSymbolContextLineEntry)); if (GetProcess()->GetTarget().GetDebugger().GetUseExternalEditor() && frame_sc.line_entry.file && frame_sc.line_entry.line != 0) { already_shown = Host::OpenFileInExternalEditor( frame_sc.line_entry.file, frame_sc.line_entry.line); } bool show_frame_info = true; bool show_source = !already_shown; FunctionOptimizationWarning(frame_sp.get()); return frame_sp->GetStatus(output_stream, show_frame_info, show_source); } return false; } else return false; } void Thread::FunctionOptimizationWarning(StackFrame *frame) { if (frame && frame->HasDebugInformation() && GetProcess()->GetWarningsOptimization()) { SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextModule); GetProcess()->PrintWarningOptimization(sc); } } lldb::StopInfoSP Thread::GetStopInfo() { if (m_destroy_called) return m_stop_info_sp; ThreadPlanSP completed_plan_sp(GetCompletedPlan()); ProcessSP process_sp(GetProcess()); const uint32_t stop_id = process_sp ? process_sp->GetStopID() : UINT32_MAX; // Here we select the stop info according to priorirty: // - m_stop_info_sp (if not trace) - preset value // - completed plan stop info - new value with plan from completed plan stack // - m_stop_info_sp (trace stop reason is OK now) // - ask GetPrivateStopInfo to set stop info bool have_valid_stop_info = m_stop_info_sp && m_stop_info_sp ->IsValid() && m_stop_info_stop_id == stop_id; bool have_valid_completed_plan = completed_plan_sp && completed_plan_sp->PlanSucceeded(); bool plan_overrides_trace = have_valid_stop_info && have_valid_completed_plan && (m_stop_info_sp->GetStopReason() == eStopReasonTrace); - + if (have_valid_stop_info && !plan_overrides_trace) { return m_stop_info_sp; } else if (have_valid_completed_plan) { return StopInfo::CreateStopReasonWithPlan( completed_plan_sp, GetReturnValueObject(), GetExpressionVariable()); } else { GetPrivateStopInfo(); return m_stop_info_sp; } } lldb::StopInfoSP Thread::GetPrivateStopInfo() { if (m_destroy_called) return m_stop_info_sp; ProcessSP process_sp(GetProcess()); if (process_sp) { const uint32_t process_stop_id = process_sp->GetStopID(); if (m_stop_info_stop_id != process_stop_id) { if (m_stop_info_sp) { if (m_stop_info_sp->IsValid() || IsStillAtLastBreakpointHit() || GetCurrentPlan()->IsVirtualStep()) SetStopInfo(m_stop_info_sp); else m_stop_info_sp.reset(); } if (!m_stop_info_sp) { if (!CalculateStopInfo()) SetStopInfo(StopInfoSP()); } } // The stop info can be manually set by calling Thread::SetStopInfo() // prior to this function ever getting called, so we can't rely on // "m_stop_info_stop_id != process_stop_id" as the condition for // the if statement below, we must also check the stop info to see // if we need to override it. See the header documentation in // Process::GetStopInfoOverrideCallback() for more information on // the stop info override callback. if (m_stop_info_override_stop_id != process_stop_id) { m_stop_info_override_stop_id = process_stop_id; if (m_stop_info_sp) { ArchSpec::StopInfoOverrideCallbackType callback = GetProcess()->GetStopInfoOverrideCallback(); if (callback) callback(*this); } } } return m_stop_info_sp; } lldb::StopReason Thread::GetStopReason() { lldb::StopInfoSP stop_info_sp(GetStopInfo()); if (stop_info_sp) return stop_info_sp->GetStopReason(); return eStopReasonNone; } bool Thread::StopInfoIsUpToDate() const { ProcessSP process_sp(GetProcess()); if (process_sp) return m_stop_info_stop_id == process_sp->GetStopID(); else return true; // Process is no longer around so stop info is always up to // date... } void Thread::ResetStopInfo() { if (m_stop_info_sp) { m_stop_info_sp.reset(); } } void Thread::SetStopInfo(const lldb::StopInfoSP &stop_info_sp) { m_stop_info_sp = stop_info_sp; if (m_stop_info_sp) { m_stop_info_sp->MakeStopInfoValid(); // If we are overriding the ShouldReportStop, do that here: if (m_override_should_notify != eLazyBoolCalculate) m_stop_info_sp->OverrideShouldNotify(m_override_should_notify == eLazyBoolYes); } ProcessSP process_sp(GetProcess()); if (process_sp) m_stop_info_stop_id = process_sp->GetStopID(); else m_stop_info_stop_id = UINT32_MAX; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); if (log) log->Printf("%p: tid = 0x%" PRIx64 ": stop info = %s (stop_id = %u)", static_cast(this), GetID(), stop_info_sp ? stop_info_sp->GetDescription() : "", m_stop_info_stop_id); } void Thread::SetShouldReportStop(Vote vote) { if (vote == eVoteNoOpinion) return; else { m_override_should_notify = (vote == eVoteYes ? eLazyBoolYes : eLazyBoolNo); if (m_stop_info_sp) m_stop_info_sp->OverrideShouldNotify(m_override_should_notify == eLazyBoolYes); } } void Thread::SetStopInfoToNothing() { // Note, we can't just NULL out the private reason, or the native thread // implementation will try to // go calculate it again. For now, just set it to a Unix Signal with an // invalid signal number. SetStopInfo( StopInfo::CreateStopReasonWithSignal(*this, LLDB_INVALID_SIGNAL_NUMBER)); } bool Thread::ThreadStoppedForAReason(void) { return (bool)GetPrivateStopInfo(); } bool Thread::CheckpointThreadState(ThreadStateCheckpoint &saved_state) { saved_state.register_backup_sp.reset(); lldb::StackFrameSP frame_sp(GetStackFrameAtIndex(0)); if (frame_sp) { lldb::RegisterCheckpointSP reg_checkpoint_sp( new RegisterCheckpoint(RegisterCheckpoint::Reason::eExpression)); if (reg_checkpoint_sp) { lldb::RegisterContextSP reg_ctx_sp(frame_sp->GetRegisterContext()); if (reg_ctx_sp && reg_ctx_sp->ReadAllRegisterValues(*reg_checkpoint_sp)) saved_state.register_backup_sp = reg_checkpoint_sp; } } if (!saved_state.register_backup_sp) return false; saved_state.stop_info_sp = GetStopInfo(); ProcessSP process_sp(GetProcess()); if (process_sp) saved_state.orig_stop_id = process_sp->GetStopID(); saved_state.current_inlined_depth = GetCurrentInlinedDepth(); saved_state.m_completed_plan_stack = m_completed_plan_stack; - + return true; } bool Thread::RestoreRegisterStateFromCheckpoint( ThreadStateCheckpoint &saved_state) { if (saved_state.register_backup_sp) { lldb::StackFrameSP frame_sp(GetStackFrameAtIndex(0)); if (frame_sp) { lldb::RegisterContextSP reg_ctx_sp(frame_sp->GetRegisterContext()); if (reg_ctx_sp) { bool ret = reg_ctx_sp->WriteAllRegisterValues(*saved_state.register_backup_sp); // Clear out all stack frames as our world just changed. ClearStackFrames(); reg_ctx_sp->InvalidateIfNeeded(true); if (m_unwinder_ap.get()) m_unwinder_ap->Clear(); return ret; } } } return false; } bool Thread::RestoreThreadStateFromCheckpoint( ThreadStateCheckpoint &saved_state) { if (saved_state.stop_info_sp) saved_state.stop_info_sp->MakeStopInfoValid(); SetStopInfo(saved_state.stop_info_sp); GetStackFrameList()->SetCurrentInlinedDepth( saved_state.current_inlined_depth); m_completed_plan_stack = saved_state.m_completed_plan_stack; return true; } StateType Thread::GetState() const { // If any other threads access this we will need a mutex for it std::lock_guard guard(m_state_mutex); return m_state; } void Thread::SetState(StateType state) { std::lock_guard guard(m_state_mutex); m_state = state; } void Thread::WillStop() { ThreadPlan *current_plan = GetCurrentPlan(); // FIXME: I may decide to disallow threads with no plans. In which // case this should go to an assert. if (!current_plan) return; current_plan->WillStop(); } void Thread::SetupForResume() { if (GetResumeState() != eStateSuspended) { // If we're at a breakpoint push the step-over breakpoint plan. Do this // before // telling the current plan it will resume, since we might change what the // current // plan is. lldb::RegisterContextSP reg_ctx_sp(GetRegisterContext()); if (reg_ctx_sp) { const addr_t thread_pc = reg_ctx_sp->GetPC(); BreakpointSiteSP bp_site_sp = GetProcess()->GetBreakpointSiteList().FindByAddress(thread_pc); if (bp_site_sp) { // Note, don't assume there's a ThreadPlanStepOverBreakpoint, the target // may not require anything // special to step over a breakpoint. ThreadPlan *cur_plan = GetCurrentPlan(); bool push_step_over_bp_plan = false; if (cur_plan->GetKind() == ThreadPlan::eKindStepOverBreakpoint) { ThreadPlanStepOverBreakpoint *bp_plan = (ThreadPlanStepOverBreakpoint *)cur_plan; if (bp_plan->GetBreakpointLoadAddress() != thread_pc) push_step_over_bp_plan = true; } else push_step_over_bp_plan = true; if (push_step_over_bp_plan) { ThreadPlanSP step_bp_plan_sp(new ThreadPlanStepOverBreakpoint(*this)); if (step_bp_plan_sp) { step_bp_plan_sp->SetPrivate(true); if (GetCurrentPlan()->RunState() != eStateStepping) { ThreadPlanStepOverBreakpoint *step_bp_plan = static_cast( step_bp_plan_sp.get()); step_bp_plan->SetAutoContinue(true); } QueueThreadPlan(step_bp_plan_sp, false); } } } } } } bool Thread::ShouldResume(StateType resume_state) { // At this point clear the completed plan stack. m_completed_plan_stack.clear(); m_discarded_plan_stack.clear(); m_override_should_notify = eLazyBoolCalculate; StateType prev_resume_state = GetTemporaryResumeState(); SetTemporaryResumeState(resume_state); lldb::ThreadSP backing_thread_sp(GetBackingThread()); if (backing_thread_sp) backing_thread_sp->SetTemporaryResumeState(resume_state); // Make sure m_stop_info_sp is valid. Don't do this for threads we suspended // in the previous run. if (prev_resume_state != eStateSuspended) GetPrivateStopInfo(); // This is a little dubious, but we are trying to limit how often we actually // fetch stop info from // the target, 'cause that slows down single stepping. So assume that if we // got to the point where // we're about to resume, and we haven't yet had to fetch the stop reason, // then it doesn't need to know // about the fact that we are resuming... const uint32_t process_stop_id = GetProcess()->GetStopID(); if (m_stop_info_stop_id == process_stop_id && (m_stop_info_sp && m_stop_info_sp->IsValid())) { StopInfo *stop_info = GetPrivateStopInfo().get(); if (stop_info) stop_info->WillResume(resume_state); } // Tell all the plans that we are about to resume in case they need to clear // any state. // We distinguish between the plan on the top of the stack and the lower // plans in case a plan needs to do any special business before it runs. bool need_to_resume = false; ThreadPlan *plan_ptr = GetCurrentPlan(); if (plan_ptr) { need_to_resume = plan_ptr->WillResume(resume_state, true); while ((plan_ptr = GetPreviousPlan(plan_ptr)) != nullptr) { plan_ptr->WillResume(resume_state, false); } // If the WillResume for the plan says we are faking a resume, then it will // have set an appropriate stop info. // In that case, don't reset it here. if (need_to_resume && resume_state != eStateSuspended) { m_stop_info_sp.reset(); } } if (need_to_resume) { ClearStackFrames(); // Let Thread subclasses do any special work they need to prior to resuming WillResume(resume_state); } return need_to_resume; } void Thread::DidResume() { SetResumeSignal(LLDB_INVALID_SIGNAL_NUMBER); } void Thread::DidStop() { SetState(eStateStopped); } bool Thread::ShouldStop(Event *event_ptr) { ThreadPlan *current_plan = GetCurrentPlan(); bool should_stop = true; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (GetResumeState() == eStateSuspended) { if (log) log->Printf("Thread::%s for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64 ", should_stop = 0 (ignore since thread was suspended)", __FUNCTION__, GetID(), GetProtocolID()); return false; } if (GetTemporaryResumeState() == eStateSuspended) { if (log) log->Printf("Thread::%s for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64 ", should_stop = 0 (ignore since thread was suspended)", __FUNCTION__, GetID(), GetProtocolID()); return false; } // Based on the current thread plan and process stop info, check if this // thread caused the process to stop. NOTE: this must take place before // the plan is moved from the current plan stack to the completed plan // stack. if (!ThreadStoppedForAReason()) { if (log) log->Printf("Thread::%s for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64 ", pc = 0x%16.16" PRIx64 ", should_stop = 0 (ignore since no stop reason)", __FUNCTION__, GetID(), GetProtocolID(), GetRegisterContext() ? GetRegisterContext()->GetPC() : LLDB_INVALID_ADDRESS); return false; } if (log) { log->Printf("Thread::%s(%p) for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64 ", pc = 0x%16.16" PRIx64, __FUNCTION__, static_cast(this), GetID(), GetProtocolID(), GetRegisterContext() ? GetRegisterContext()->GetPC() : LLDB_INVALID_ADDRESS); log->Printf("^^^^^^^^ Thread::ShouldStop Begin ^^^^^^^^"); StreamString s; s.IndentMore(); DumpThreadPlans(&s); log->Printf("Plan stack initial state:\n%s", s.GetData()); } // The top most plan always gets to do the trace log... current_plan->DoTraceLog(); // First query the stop info's ShouldStopSynchronous. This handles // "synchronous" stop reasons, for example the breakpoint // command on internal breakpoints. If a synchronous stop reason says we // should not stop, then we don't have to // do any more work on this stop. StopInfoSP private_stop_info(GetPrivateStopInfo()); if (private_stop_info && !private_stop_info->ShouldStopSynchronous(event_ptr)) { if (log) log->Printf("StopInfo::ShouldStop async callback says we should not " "stop, returning ShouldStop of false."); return false; } // If we've already been restarted, don't query the plans since the state they // would examine is not current. if (Process::ProcessEventData::GetRestartedFromEvent(event_ptr)) return false; // Before the plans see the state of the world, calculate the current inlined // depth. GetStackFrameList()->CalculateCurrentInlinedDepth(); // If the base plan doesn't understand why we stopped, then we have to find a // plan that does. // If that plan is still working, then we don't need to do any more work. If // the plan that explains // the stop is done, then we should pop all the plans below it, and pop it, // and then let the plans above it decide // whether they still need to do more work. bool done_processing_current_plan = false; if (!current_plan->PlanExplainsStop(event_ptr)) { if (current_plan->TracerExplainsStop()) { done_processing_current_plan = true; should_stop = false; } else { // If the current plan doesn't explain the stop, then find one that // does and let it handle the situation. ThreadPlan *plan_ptr = current_plan; while ((plan_ptr = GetPreviousPlan(plan_ptr)) != nullptr) { if (plan_ptr->PlanExplainsStop(event_ptr)) { should_stop = plan_ptr->ShouldStop(event_ptr); // plan_ptr explains the stop, next check whether plan_ptr is done, if // so, then we should take it // and all the plans below it off the stack. if (plan_ptr->MischiefManaged()) { // We're going to pop the plans up to and including the plan that // explains the stop. ThreadPlan *prev_plan_ptr = GetPreviousPlan(plan_ptr); do { if (should_stop) current_plan->WillStop(); PopPlan(); } while ((current_plan = GetCurrentPlan()) != prev_plan_ptr); // Now, if the responsible plan was not "Okay to discard" then we're // done, // otherwise we forward this to the next plan in the stack below. done_processing_current_plan = (plan_ptr->IsMasterPlan() && !plan_ptr->OkayToDiscard()); } else done_processing_current_plan = true; break; } } } } if (!done_processing_current_plan) { bool over_ride_stop = current_plan->ShouldAutoContinue(event_ptr); if (log) log->Printf("Plan %s explains stop, auto-continue %i.", current_plan->GetName(), over_ride_stop); // We're starting from the base plan, so just let it decide; if (PlanIsBasePlan(current_plan)) { should_stop = current_plan->ShouldStop(event_ptr); if (log) log->Printf("Base plan says should stop: %i.", should_stop); } else { // Otherwise, don't let the base plan override what the other plans say to // do, since // presumably if there were other plans they would know what to do... while (1) { if (PlanIsBasePlan(current_plan)) break; should_stop = current_plan->ShouldStop(event_ptr); if (log) log->Printf("Plan %s should stop: %d.", current_plan->GetName(), should_stop); if (current_plan->MischiefManaged()) { if (should_stop) current_plan->WillStop(); // If a Master Plan wants to stop, and wants to stick on the stack, we // let it. // Otherwise, see if the plan's parent wants to stop. if (should_stop && current_plan->IsMasterPlan() && !current_plan->OkayToDiscard()) { PopPlan(); break; } else { PopPlan(); current_plan = GetCurrentPlan(); if (current_plan == nullptr) { break; } } } else { break; } } } if (over_ride_stop) should_stop = false; } // One other potential problem is that we set up a master plan, then stop in // before it is complete - for instance // by hitting a breakpoint during a step-over - then do some step/finish/etc // operations that wind up // past the end point condition of the initial plan. We don't want to strand // the original plan on the stack, // This code clears stale plans off the stack. if (should_stop) { ThreadPlan *plan_ptr = GetCurrentPlan(); // Discard the stale plans and all plans below them in the stack, // plus move the completed plans to the completed plan stack while (!PlanIsBasePlan(plan_ptr)) { bool stale = plan_ptr->IsPlanStale(); ThreadPlan *examined_plan = plan_ptr; plan_ptr = GetPreviousPlan(examined_plan); if (stale) { if (log) log->Printf( "Plan %s being discarded in cleanup, it says it is already done.", examined_plan->GetName()); while (GetCurrentPlan() != examined_plan) { DiscardPlan(); } if (examined_plan->IsPlanComplete()) { // plan is complete but does not explain the stop (example: step to a line // with breakpoint), let us move the plan to completed_plan_stack anyway PopPlan(); } else DiscardPlan(); } } } if (log) { StreamString s; s.IndentMore(); DumpThreadPlans(&s); log->Printf("Plan stack final state:\n%s", s.GetData()); log->Printf("vvvvvvvv Thread::ShouldStop End (returning %i) vvvvvvvv", should_stop); } return should_stop; } Vote Thread::ShouldReportStop(Event *event_ptr) { StateType thread_state = GetResumeState(); StateType temp_thread_state = GetTemporaryResumeState(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (thread_state == eStateSuspended || thread_state == eStateInvalid) { if (log) log->Printf("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 ": returning vote %i (state was suspended or invalid)", GetID(), eVoteNoOpinion); return eVoteNoOpinion; } if (temp_thread_state == eStateSuspended || temp_thread_state == eStateInvalid) { if (log) log->Printf( "Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 ": returning vote %i (temporary state was suspended or invalid)", GetID(), eVoteNoOpinion); return eVoteNoOpinion; } if (!ThreadStoppedForAReason()) { if (log) log->Printf("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 ": returning vote %i (thread didn't stop for a reason.)", GetID(), eVoteNoOpinion); return eVoteNoOpinion; } if (m_completed_plan_stack.size() > 0) { // Don't use GetCompletedPlan here, since that suppresses private plans. if (log) log->Printf("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 ": returning vote for complete stack's back plan", GetID()); return m_completed_plan_stack.back()->ShouldReportStop(event_ptr); } else { Vote thread_vote = eVoteNoOpinion; ThreadPlan *plan_ptr = GetCurrentPlan(); while (1) { if (plan_ptr->PlanExplainsStop(event_ptr)) { thread_vote = plan_ptr->ShouldReportStop(event_ptr); break; } if (PlanIsBasePlan(plan_ptr)) break; else plan_ptr = GetPreviousPlan(plan_ptr); } if (log) log->Printf("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 ": returning vote %i for current plan", GetID(), thread_vote); return thread_vote; } } Vote Thread::ShouldReportRun(Event *event_ptr) { StateType thread_state = GetResumeState(); if (thread_state == eStateSuspended || thread_state == eStateInvalid) { return eVoteNoOpinion; } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (m_completed_plan_stack.size() > 0) { // Don't use GetCompletedPlan here, since that suppresses private plans. if (log) log->Printf("Current Plan for thread %d(%p) (0x%4.4" PRIx64 ", %s): %s being asked whether we should report run.", GetIndexID(), static_cast(this), GetID(), StateAsCString(GetTemporaryResumeState()), m_completed_plan_stack.back()->GetName()); return m_completed_plan_stack.back()->ShouldReportRun(event_ptr); } else { if (log) log->Printf("Current Plan for thread %d(%p) (0x%4.4" PRIx64 ", %s): %s being asked whether we should report run.", GetIndexID(), static_cast(this), GetID(), StateAsCString(GetTemporaryResumeState()), GetCurrentPlan()->GetName()); return GetCurrentPlan()->ShouldReportRun(event_ptr); } } bool Thread::MatchesSpec(const ThreadSpec *spec) { return (spec == nullptr) ? true : spec->ThreadPassesBasicTests(*this); } void Thread::PushPlan(ThreadPlanSP &thread_plan_sp) { if (thread_plan_sp) { // If the thread plan doesn't already have a tracer, give it its parent's // tracer: if (!thread_plan_sp->GetThreadPlanTracer()) thread_plan_sp->SetThreadPlanTracer( m_plan_stack.back()->GetThreadPlanTracer()); m_plan_stack.push_back(thread_plan_sp); thread_plan_sp->DidPush(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log) { StreamString s; thread_plan_sp->GetDescription(&s, lldb::eDescriptionLevelFull); log->Printf("Thread::PushPlan(0x%p): \"%s\", tid = 0x%4.4" PRIx64 ".", static_cast(this), s.GetData(), thread_plan_sp->GetThread().GetID()); } } } void Thread::PopPlan() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (m_plan_stack.size() <= 1) return; else { ThreadPlanSP &plan = m_plan_stack.back(); if (log) { log->Printf("Popping plan: \"%s\", tid = 0x%4.4" PRIx64 ".", plan->GetName(), plan->GetThread().GetID()); } m_completed_plan_stack.push_back(plan); plan->WillPop(); m_plan_stack.pop_back(); } } void Thread::DiscardPlan() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (m_plan_stack.size() > 1) { ThreadPlanSP &plan = m_plan_stack.back(); if (log) log->Printf("Discarding plan: \"%s\", tid = 0x%4.4" PRIx64 ".", plan->GetName(), plan->GetThread().GetID()); m_discarded_plan_stack.push_back(plan); plan->WillPop(); m_plan_stack.pop_back(); } } ThreadPlan *Thread::GetCurrentPlan() { // There will always be at least the base plan. If somebody is mucking with a // thread with an empty plan stack, we should assert right away. return m_plan_stack.empty() ? nullptr : m_plan_stack.back().get(); } ThreadPlanSP Thread::GetCompletedPlan() { ThreadPlanSP empty_plan_sp; if (!m_completed_plan_stack.empty()) { for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) { ThreadPlanSP completed_plan_sp; completed_plan_sp = m_completed_plan_stack[i]; if (!completed_plan_sp->GetPrivate()) return completed_plan_sp; } } return empty_plan_sp; } ValueObjectSP Thread::GetReturnValueObject() { if (!m_completed_plan_stack.empty()) { for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) { ValueObjectSP return_valobj_sp; return_valobj_sp = m_completed_plan_stack[i]->GetReturnValueObject(); if (return_valobj_sp) return return_valobj_sp; } } return ValueObjectSP(); } ExpressionVariableSP Thread::GetExpressionVariable() { if (!m_completed_plan_stack.empty()) { for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) { ExpressionVariableSP expression_variable_sp; expression_variable_sp = m_completed_plan_stack[i]->GetExpressionVariable(); if (expression_variable_sp) return expression_variable_sp; } } return ExpressionVariableSP(); } bool Thread::IsThreadPlanDone(ThreadPlan *plan) { if (!m_completed_plan_stack.empty()) { for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) { if (m_completed_plan_stack[i].get() == plan) return true; } } return false; } bool Thread::WasThreadPlanDiscarded(ThreadPlan *plan) { if (!m_discarded_plan_stack.empty()) { for (int i = m_discarded_plan_stack.size() - 1; i >= 0; i--) { if (m_discarded_plan_stack[i].get() == plan) return true; } } return false; } bool Thread::CompletedPlanOverridesBreakpoint() { return (!m_completed_plan_stack.empty()) ; } ThreadPlan *Thread::GetPreviousPlan(ThreadPlan *current_plan) { if (current_plan == nullptr) return nullptr; int stack_size = m_completed_plan_stack.size(); for (int i = stack_size - 1; i > 0; i--) { if (current_plan == m_completed_plan_stack[i].get()) return m_completed_plan_stack[i - 1].get(); } if (stack_size > 0 && m_completed_plan_stack[0].get() == current_plan) { return GetCurrentPlan(); } stack_size = m_plan_stack.size(); for (int i = stack_size - 1; i > 0; i--) { if (current_plan == m_plan_stack[i].get()) return m_plan_stack[i - 1].get(); } return nullptr; } void Thread::QueueThreadPlan(ThreadPlanSP &thread_plan_sp, bool abort_other_plans) { if (abort_other_plans) DiscardThreadPlans(true); PushPlan(thread_plan_sp); } void Thread::EnableTracer(bool value, bool single_stepping) { int stack_size = m_plan_stack.size(); for (int i = 0; i < stack_size; i++) { if (m_plan_stack[i]->GetThreadPlanTracer()) { m_plan_stack[i]->GetThreadPlanTracer()->EnableTracing(value); m_plan_stack[i]->GetThreadPlanTracer()->EnableSingleStep(single_stepping); } } } void Thread::SetTracer(lldb::ThreadPlanTracerSP &tracer_sp) { int stack_size = m_plan_stack.size(); for (int i = 0; i < stack_size; i++) m_plan_stack[i]->SetThreadPlanTracer(tracer_sp); } bool Thread::DiscardUserThreadPlansUpToIndex(uint32_t thread_index) { // Count the user thread plans from the back end to get the number of the one // we want // to discard: uint32_t idx = 0; ThreadPlan *up_to_plan_ptr = nullptr; for (ThreadPlanSP plan_sp : m_plan_stack) { if (plan_sp->GetPrivate()) continue; if (idx == thread_index) { up_to_plan_ptr = plan_sp.get(); break; } else idx++; } if (up_to_plan_ptr == nullptr) return false; DiscardThreadPlansUpToPlan(up_to_plan_ptr); return true; } void Thread::DiscardThreadPlansUpToPlan(lldb::ThreadPlanSP &up_to_plan_sp) { DiscardThreadPlansUpToPlan(up_to_plan_sp.get()); } void Thread::DiscardThreadPlansUpToPlan(ThreadPlan *up_to_plan_ptr) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log) log->Printf("Discarding thread plans for thread tid = 0x%4.4" PRIx64 ", up to %p", GetID(), static_cast(up_to_plan_ptr)); int stack_size = m_plan_stack.size(); // If the input plan is nullptr, discard all plans. Otherwise make sure this // plan is in the // stack, and if so discard up to and including it. if (up_to_plan_ptr == nullptr) { for (int i = stack_size - 1; i > 0; i--) DiscardPlan(); } else { bool found_it = false; for (int i = stack_size - 1; i > 0; i--) { if (m_plan_stack[i].get() == up_to_plan_ptr) found_it = true; } if (found_it) { bool last_one = false; for (int i = stack_size - 1; i > 0 && !last_one; i--) { if (GetCurrentPlan() == up_to_plan_ptr) last_one = true; DiscardPlan(); } } } } void Thread::DiscardThreadPlans(bool force) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log) { log->Printf("Discarding thread plans for thread (tid = 0x%4.4" PRIx64 ", force %d)", GetID(), force); } if (force) { int stack_size = m_plan_stack.size(); for (int i = stack_size - 1; i > 0; i--) { DiscardPlan(); } return; } while (1) { int master_plan_idx; bool discard = true; // Find the first master plan, see if it wants discarding, and if yes // discard up to it. for (master_plan_idx = m_plan_stack.size() - 1; master_plan_idx >= 0; master_plan_idx--) { if (m_plan_stack[master_plan_idx]->IsMasterPlan()) { discard = m_plan_stack[master_plan_idx]->OkayToDiscard(); break; } } if (discard) { // First pop all the dependent plans: for (int i = m_plan_stack.size() - 1; i > master_plan_idx; i--) { // FIXME: Do we need a finalize here, or is the rule that // "PrepareForStop" // for the plan leaves it in a state that it is safe to pop the plan // with no more notice? DiscardPlan(); } // Now discard the master plan itself. // The bottom-most plan never gets discarded. "OkayToDiscard" for it // means // discard it's dependent plans, but not it... if (master_plan_idx > 0) { DiscardPlan(); } } else { // If the master plan doesn't want to get discarded, then we're done. break; } } } bool Thread::PlanIsBasePlan(ThreadPlan *plan_ptr) { if (plan_ptr->IsBasePlan()) return true; else if (m_plan_stack.size() == 0) return false; else return m_plan_stack[0].get() == plan_ptr; } Status Thread::UnwindInnermostExpression() { Status error; int stack_size = m_plan_stack.size(); // If the input plan is nullptr, discard all plans. Otherwise make sure this // plan is in the // stack, and if so discard up to and including it. for (int i = stack_size - 1; i > 0; i--) { if (m_plan_stack[i]->GetKind() == ThreadPlan::eKindCallFunction) { DiscardThreadPlansUpToPlan(m_plan_stack[i].get()); return error; } } error.SetErrorString("No expressions currently active on this thread"); return error; } ThreadPlanSP Thread::QueueFundamentalPlan(bool abort_other_plans) { ThreadPlanSP thread_plan_sp(new ThreadPlanBase(*this)); QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } ThreadPlanSP Thread::QueueThreadPlanForStepSingleInstruction( bool step_over, bool abort_other_plans, bool stop_other_threads) { ThreadPlanSP thread_plan_sp(new ThreadPlanStepInstruction( *this, step_over, stop_other_threads, eVoteNoOpinion, eVoteNoOpinion)); QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } ThreadPlanSP Thread::QueueThreadPlanForStepOverRange( bool abort_other_plans, const AddressRange &range, const SymbolContext &addr_context, lldb::RunMode stop_other_threads, LazyBool step_out_avoids_code_withoug_debug_info) { ThreadPlanSP thread_plan_sp; thread_plan_sp.reset(new ThreadPlanStepOverRange( *this, range, addr_context, stop_other_threads, step_out_avoids_code_withoug_debug_info)); QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } // Call the QueueThreadPlanForStepOverRange method which takes an address range. ThreadPlanSP Thread::QueueThreadPlanForStepOverRange( bool abort_other_plans, const LineEntry &line_entry, const SymbolContext &addr_context, lldb::RunMode stop_other_threads, LazyBool step_out_avoids_code_withoug_debug_info) { return QueueThreadPlanForStepOverRange( abort_other_plans, line_entry.GetSameLineContiguousAddressRange(), addr_context, stop_other_threads, step_out_avoids_code_withoug_debug_info); } ThreadPlanSP Thread::QueueThreadPlanForStepInRange( bool abort_other_plans, const AddressRange &range, const SymbolContext &addr_context, const char *step_in_target, lldb::RunMode stop_other_threads, LazyBool step_in_avoids_code_without_debug_info, LazyBool step_out_avoids_code_without_debug_info) { ThreadPlanSP thread_plan_sp( new ThreadPlanStepInRange(*this, range, addr_context, stop_other_threads, step_in_avoids_code_without_debug_info, step_out_avoids_code_without_debug_info)); ThreadPlanStepInRange *plan = static_cast(thread_plan_sp.get()); if (step_in_target) plan->SetStepInTarget(step_in_target); QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } // Call the QueueThreadPlanForStepInRange method which takes an address range. ThreadPlanSP Thread::QueueThreadPlanForStepInRange( bool abort_other_plans, const LineEntry &line_entry, const SymbolContext &addr_context, const char *step_in_target, lldb::RunMode stop_other_threads, LazyBool step_in_avoids_code_without_debug_info, LazyBool step_out_avoids_code_without_debug_info) { return QueueThreadPlanForStepInRange( abort_other_plans, line_entry.GetSameLineContiguousAddressRange(), addr_context, step_in_target, stop_other_threads, step_in_avoids_code_without_debug_info, step_out_avoids_code_without_debug_info); } ThreadPlanSP Thread::QueueThreadPlanForStepOut( bool abort_other_plans, SymbolContext *addr_context, bool first_insn, bool stop_other_threads, Vote stop_vote, Vote run_vote, uint32_t frame_idx, LazyBool step_out_avoids_code_without_debug_info) { ThreadPlanSP thread_plan_sp(new ThreadPlanStepOut( *this, addr_context, first_insn, stop_other_threads, stop_vote, run_vote, frame_idx, step_out_avoids_code_without_debug_info)); if (thread_plan_sp->ValidatePlan(nullptr)) { QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } else { return ThreadPlanSP(); } } ThreadPlanSP Thread::QueueThreadPlanForStepOutNoShouldStop( bool abort_other_plans, SymbolContext *addr_context, bool first_insn, bool stop_other_threads, Vote stop_vote, Vote run_vote, uint32_t frame_idx, bool continue_to_next_branch) { const bool calculate_return_value = false; // No need to calculate the return value here. ThreadPlanSP thread_plan_sp(new ThreadPlanStepOut( *this, addr_context, first_insn, stop_other_threads, stop_vote, run_vote, frame_idx, eLazyBoolNo, continue_to_next_branch, calculate_return_value)); ThreadPlanStepOut *new_plan = static_cast(thread_plan_sp.get()); new_plan->ClearShouldStopHereCallbacks(); if (thread_plan_sp->ValidatePlan(nullptr)) { QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } else { return ThreadPlanSP(); } } ThreadPlanSP Thread::QueueThreadPlanForStepThrough(StackID &return_stack_id, bool abort_other_plans, bool stop_other_threads) { ThreadPlanSP thread_plan_sp( new ThreadPlanStepThrough(*this, return_stack_id, stop_other_threads)); if (!thread_plan_sp || !thread_plan_sp->ValidatePlan(nullptr)) return ThreadPlanSP(); QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } ThreadPlanSP Thread::QueueThreadPlanForRunToAddress(bool abort_other_plans, Address &target_addr, bool stop_other_threads) { ThreadPlanSP thread_plan_sp( new ThreadPlanRunToAddress(*this, target_addr, stop_other_threads)); QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } ThreadPlanSP Thread::QueueThreadPlanForStepUntil(bool abort_other_plans, lldb::addr_t *address_list, size_t num_addresses, bool stop_other_threads, uint32_t frame_idx) { ThreadPlanSP thread_plan_sp(new ThreadPlanStepUntil( *this, address_list, num_addresses, stop_other_threads, frame_idx)); QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } lldb::ThreadPlanSP Thread::QueueThreadPlanForStepScripted( bool abort_other_plans, const char *class_name, bool stop_other_threads) { ThreadPlanSP thread_plan_sp(new ThreadPlanPython(*this, class_name)); QueueThreadPlan(thread_plan_sp, abort_other_plans); // This seems a little funny, but I don't want to have to split up the // constructor and the // DidPush in the scripted plan, that seems annoying. // That means the constructor has to be in DidPush. // So I have to validate the plan AFTER pushing it, and then take it off // again... if (!thread_plan_sp->ValidatePlan(nullptr)) { DiscardThreadPlansUpToPlan(thread_plan_sp); return ThreadPlanSP(); } else return thread_plan_sp; } uint32_t Thread::GetIndexID() const { return m_index_id; } static void PrintPlanElement(Stream *s, const ThreadPlanSP &plan, lldb::DescriptionLevel desc_level, int32_t elem_idx) { s->IndentMore(); s->Indent(); s->Printf("Element %d: ", elem_idx); plan->GetDescription(s, desc_level); s->EOL(); s->IndentLess(); } static void PrintPlanStack(Stream *s, const std::vector &plan_stack, lldb::DescriptionLevel desc_level, bool include_internal) { int32_t print_idx = 0; for (ThreadPlanSP plan_sp : plan_stack) { if (include_internal || !plan_sp->GetPrivate()) { PrintPlanElement(s, plan_sp, desc_level, print_idx++); } } } void Thread::DumpThreadPlans(Stream *s, lldb::DescriptionLevel desc_level, bool include_internal, bool ignore_boring_threads) const { uint32_t stack_size; if (ignore_boring_threads) { uint32_t stack_size = m_plan_stack.size(); uint32_t completed_stack_size = m_completed_plan_stack.size(); uint32_t discarded_stack_size = m_discarded_plan_stack.size(); if (stack_size == 1 && completed_stack_size == 0 && discarded_stack_size == 0) { s->Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", GetIndexID(), GetID()); s->IndentMore(); s->Indent(); s->Printf("No active thread plans\n"); s->IndentLess(); return; } } s->Indent(); s->Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", GetIndexID(), GetID()); s->IndentMore(); s->Indent(); s->Printf("Active plan stack:\n"); PrintPlanStack(s, m_plan_stack, desc_level, include_internal); stack_size = m_completed_plan_stack.size(); if (stack_size > 0) { s->Indent(); s->Printf("Completed Plan Stack:\n"); PrintPlanStack(s, m_completed_plan_stack, desc_level, include_internal); } stack_size = m_discarded_plan_stack.size(); if (stack_size > 0) { s->Indent(); s->Printf("Discarded Plan Stack:\n"); PrintPlanStack(s, m_discarded_plan_stack, desc_level, include_internal); } s->IndentLess(); } TargetSP Thread::CalculateTarget() { TargetSP target_sp; ProcessSP process_sp(GetProcess()); if (process_sp) target_sp = process_sp->CalculateTarget(); return target_sp; } ProcessSP Thread::CalculateProcess() { return GetProcess(); } ThreadSP Thread::CalculateThread() { return shared_from_this(); } StackFrameSP Thread::CalculateStackFrame() { return StackFrameSP(); } void Thread::CalculateExecutionContext(ExecutionContext &exe_ctx) { exe_ctx.SetContext(shared_from_this()); } StackFrameListSP Thread::GetStackFrameList() { StackFrameListSP frame_list_sp; std::lock_guard guard(m_frame_mutex); if (m_curr_frames_sp) { frame_list_sp = m_curr_frames_sp; } else { frame_list_sp.reset(new StackFrameList(*this, m_prev_frames_sp, true)); m_curr_frames_sp = frame_list_sp; } return frame_list_sp; } void Thread::ClearStackFrames() { std::lock_guard guard(m_frame_mutex); Unwind *unwinder = GetUnwinder(); if (unwinder) unwinder->Clear(); // Only store away the old "reference" StackFrameList if we got all its // frames: // FIXME: At some point we can try to splice in the frames we have fetched // into // the new frame as we make it, but let's not try that now. if (m_curr_frames_sp && m_curr_frames_sp->GetAllFramesFetched()) m_prev_frames_sp.swap(m_curr_frames_sp); m_curr_frames_sp.reset(); m_extended_info.reset(); m_extended_info_fetched = false; } lldb::StackFrameSP Thread::GetFrameWithConcreteFrameIndex(uint32_t unwind_idx) { return GetStackFrameList()->GetFrameWithConcreteFrameIndex(unwind_idx); } Status Thread::ReturnFromFrameWithIndex(uint32_t frame_idx, lldb::ValueObjectSP return_value_sp, bool broadcast) { StackFrameSP frame_sp = GetStackFrameAtIndex(frame_idx); Status return_error; if (!frame_sp) { return_error.SetErrorStringWithFormat( "Could not find frame with index %d in thread 0x%" PRIx64 ".", frame_idx, GetID()); } return ReturnFromFrame(frame_sp, return_value_sp, broadcast); } Status Thread::ReturnFromFrame(lldb::StackFrameSP frame_sp, lldb::ValueObjectSP return_value_sp, bool broadcast) { Status return_error; if (!frame_sp) { return_error.SetErrorString("Can't return to a null frame."); return return_error; } Thread *thread = frame_sp->GetThread().get(); uint32_t older_frame_idx = frame_sp->GetFrameIndex() + 1; StackFrameSP older_frame_sp = thread->GetStackFrameAtIndex(older_frame_idx); if (!older_frame_sp) { return_error.SetErrorString("No older frame to return to."); return return_error; } if (return_value_sp) { lldb::ABISP abi = thread->GetProcess()->GetABI(); if (!abi) { return_error.SetErrorString("Could not find ABI to set return value."); return return_error; } SymbolContext sc = frame_sp->GetSymbolContext(eSymbolContextFunction); // FIXME: ValueObject::Cast doesn't currently work correctly, at least not // for scalars. // Turn that back on when that works. if (/* DISABLES CODE */ (0) && sc.function != nullptr) { Type *function_type = sc.function->GetType(); if (function_type) { CompilerType return_type = sc.function->GetCompilerType().GetFunctionReturnType(); if (return_type) { StreamString s; return_type.DumpTypeDescription(&s); ValueObjectSP cast_value_sp = return_value_sp->Cast(return_type); if (cast_value_sp) { cast_value_sp->SetFormat(eFormatHex); return_value_sp = cast_value_sp; } } } } return_error = abi->SetReturnValueObject(older_frame_sp, return_value_sp); if (!return_error.Success()) return return_error; } // Now write the return registers for the chosen frame: // Note, we can't use ReadAllRegisterValues->WriteAllRegisterValues, since the // read & write // cook their data StackFrameSP youngest_frame_sp = thread->GetStackFrameAtIndex(0); if (youngest_frame_sp) { lldb::RegisterContextSP reg_ctx_sp(youngest_frame_sp->GetRegisterContext()); if (reg_ctx_sp) { bool copy_success = reg_ctx_sp->CopyFromRegisterContext( older_frame_sp->GetRegisterContext()); if (copy_success) { thread->DiscardThreadPlans(true); thread->ClearStackFrames(); if (broadcast && EventTypeHasListeners(eBroadcastBitStackChanged)) BroadcastEvent(eBroadcastBitStackChanged, new ThreadEventData(this->shared_from_this())); } else { return_error.SetErrorString("Could not reset register values."); } } else { return_error.SetErrorString("Frame has no register context."); } } else { return_error.SetErrorString("Returned past top frame."); } return return_error; } static void DumpAddressList(Stream &s, const std::vector
&list, ExecutionContextScope *exe_scope) { for (size_t n = 0; n < list.size(); n++) { s << "\t"; list[n].Dump(&s, exe_scope, Address::DumpStyleResolvedDescription, Address::DumpStyleSectionNameOffset); s << "\n"; } } Status Thread::JumpToLine(const FileSpec &file, uint32_t line, bool can_leave_function, std::string *warnings) { ExecutionContext exe_ctx(GetStackFrameAtIndex(0)); Target *target = exe_ctx.GetTargetPtr(); TargetSP target_sp = exe_ctx.GetTargetSP(); RegisterContext *reg_ctx = exe_ctx.GetRegisterContext(); StackFrame *frame = exe_ctx.GetFramePtr(); const SymbolContext &sc = frame->GetSymbolContext(eSymbolContextFunction); // Find candidate locations. std::vector
candidates, within_function, outside_function; target->GetImages().FindAddressesForLine(target_sp, file, line, sc.function, within_function, outside_function); // If possible, we try and stay within the current function. // Within a function, we accept multiple locations (optimized code may do // this, // there's no solution here so we do the best we can). // However if we're trying to leave the function, we don't know how to pick // the // right location, so if there's more than one then we bail. if (!within_function.empty()) candidates = within_function; else if (outside_function.size() == 1 && can_leave_function) candidates = outside_function; // Check if we got anything. if (candidates.empty()) { if (outside_function.empty()) { return Status("Cannot locate an address for %s:%i.", file.GetFilename().AsCString(), line); } else if (outside_function.size() == 1) { return Status("%s:%i is outside the current function.", file.GetFilename().AsCString(), line); } else { StreamString sstr; DumpAddressList(sstr, outside_function, target); return Status("%s:%i has multiple candidate locations:\n%s", file.GetFilename().AsCString(), line, sstr.GetData()); } } // Accept the first location, warn about any others. Address dest = candidates[0]; if (warnings && candidates.size() > 1) { StreamString sstr; sstr.Printf("%s:%i appears multiple times in this function, selecting the " "first location:\n", file.GetFilename().AsCString(), line); DumpAddressList(sstr, candidates, target); *warnings = sstr.GetString(); } if (!reg_ctx->SetPC(dest)) return Status("Cannot change PC to target address."); return Status(); } void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx, bool stop_format) { ExecutionContext exe_ctx(shared_from_this()); Process *process = exe_ctx.GetProcessPtr(); if (process == nullptr) return; StackFrameSP frame_sp; SymbolContext frame_sc; if (frame_idx != LLDB_INVALID_FRAME_ID) { frame_sp = GetStackFrameAtIndex(frame_idx); if (frame_sp) { exe_ctx.SetFrameSP(frame_sp); frame_sc = frame_sp->GetSymbolContext(eSymbolContextEverything); } } const FormatEntity::Entry *thread_format; if (stop_format) thread_format = exe_ctx.GetTargetRef().GetDebugger().GetThreadStopFormat(); else thread_format = exe_ctx.GetTargetRef().GetDebugger().GetThreadFormat(); assert(thread_format); FormatEntity::Format(*thread_format, strm, frame_sp ? &frame_sc : nullptr, &exe_ctx, nullptr, nullptr, false, false); } void Thread::SettingsInitialize() {} void Thread::SettingsTerminate() {} lldb::addr_t Thread::GetThreadPointer() { return LLDB_INVALID_ADDRESS; } addr_t Thread::GetThreadLocalData(const ModuleSP module, lldb::addr_t tls_file_addr) { // The default implementation is to ask the dynamic loader for it. // This can be overridden for specific platforms. DynamicLoader *loader = GetProcess()->GetDynamicLoader(); if (loader) return loader->GetThreadLocalData(module, shared_from_this(), tls_file_addr); else return LLDB_INVALID_ADDRESS; } bool Thread::SafeToCallFunctions() { Process *process = GetProcess().get(); if (process) { SystemRuntime *runtime = process->GetSystemRuntime(); if (runtime) { return runtime->SafeToCallFunctionsOnThisThread(shared_from_this()); } } return true; } lldb::StackFrameSP Thread::GetStackFrameSPForStackFramePtr(StackFrame *stack_frame_ptr) { return GetStackFrameList()->GetStackFrameSPForStackFramePtr(stack_frame_ptr); } const char *Thread::StopReasonAsCString(lldb::StopReason reason) { switch (reason) { case eStopReasonInvalid: return "invalid"; case eStopReasonNone: return "none"; case eStopReasonTrace: return "trace"; case eStopReasonBreakpoint: return "breakpoint"; case eStopReasonWatchpoint: return "watchpoint"; case eStopReasonSignal: return "signal"; case eStopReasonException: return "exception"; case eStopReasonExec: return "exec"; case eStopReasonPlanComplete: return "plan complete"; case eStopReasonThreadExiting: return "thread exiting"; case eStopReasonInstrumentation: return "instrumentation break"; } static char unknown_state_string[64]; snprintf(unknown_state_string, sizeof(unknown_state_string), "StopReason = %i", reason); return unknown_state_string; } const char *Thread::RunModeAsCString(lldb::RunMode mode) { switch (mode) { case eOnlyThisThread: return "only this thread"; case eAllThreads: return "all threads"; case eOnlyDuringStepping: return "only during stepping"; } static char unknown_state_string[64]; snprintf(unknown_state_string, sizeof(unknown_state_string), "RunMode = %i", mode); return unknown_state_string; } size_t Thread::GetStatus(Stream &strm, uint32_t start_frame, uint32_t num_frames, uint32_t num_frames_with_source, bool stop_format) { ExecutionContext exe_ctx(shared_from_this()); Target *target = exe_ctx.GetTargetPtr(); Process *process = exe_ctx.GetProcessPtr(); size_t num_frames_shown = 0; strm.Indent(); bool is_selected = false; if (process) { if (process->GetThreadList().GetSelectedThread().get() == this) is_selected = true; } strm.Printf("%c ", is_selected ? '*' : ' '); if (target && target->GetDebugger().GetUseExternalEditor()) { StackFrameSP frame_sp = GetStackFrameAtIndex(start_frame); if (frame_sp) { SymbolContext frame_sc( frame_sp->GetSymbolContext(eSymbolContextLineEntry)); if (frame_sc.line_entry.line != 0 && frame_sc.line_entry.file) { Host::OpenFileInExternalEditor(frame_sc.line_entry.file, frame_sc.line_entry.line); } } } DumpUsingSettingsFormat(strm, start_frame, stop_format); if (num_frames > 0) { strm.IndentMore(); const bool show_frame_info = true; const char *selected_frame_marker = nullptr; if (num_frames == 1 || (GetID() != GetProcess()->GetThreadList().GetSelectedThread()->GetID())) strm.IndentMore(); else selected_frame_marker = "* "; num_frames_shown = GetStackFrameList()->GetStatus( strm, start_frame, num_frames, show_frame_info, num_frames_with_source, selected_frame_marker); if (num_frames == 1) strm.IndentLess(); strm.IndentLess(); } return num_frames_shown; } bool Thread::GetDescription(Stream &strm, lldb::DescriptionLevel level, bool print_json_thread, bool print_json_stopinfo) { const bool stop_format = false; DumpUsingSettingsFormat(strm, 0, stop_format); strm.Printf("\n"); StructuredData::ObjectSP thread_info = GetExtendedInfo(); if (print_json_thread || print_json_stopinfo) { if (thread_info && print_json_thread) { thread_info->Dump(strm); strm.Printf("\n"); } if (print_json_stopinfo && m_stop_info_sp) { StructuredData::ObjectSP stop_info = m_stop_info_sp->GetExtendedInfo(); if (stop_info) { stop_info->Dump(strm); strm.Printf("\n"); } } return true; } if (thread_info) { StructuredData::ObjectSP activity = thread_info->GetObjectForDotSeparatedPath("activity"); StructuredData::ObjectSP breadcrumb = thread_info->GetObjectForDotSeparatedPath("breadcrumb"); StructuredData::ObjectSP messages = thread_info->GetObjectForDotSeparatedPath("trace_messages"); bool printed_activity = false; - if (activity && - activity->GetType() == StructuredData::Type::eTypeDictionary) { + if (activity && activity->GetType() == eStructuredDataTypeDictionary) { StructuredData::Dictionary *activity_dict = activity->GetAsDictionary(); StructuredData::ObjectSP id = activity_dict->GetValueForKey("id"); StructuredData::ObjectSP name = activity_dict->GetValueForKey("name"); - if (name && name->GetType() == StructuredData::Type::eTypeString && id && - id->GetType() == StructuredData::Type::eTypeInteger) { + if (name && name->GetType() == eStructuredDataTypeString && id && + id->GetType() == eStructuredDataTypeInteger) { strm.Format(" Activity '{0}', {1:x}\n", name->GetAsString()->GetValue(), id->GetAsInteger()->GetValue()); } printed_activity = true; } bool printed_breadcrumb = false; - if (breadcrumb && - breadcrumb->GetType() == StructuredData::Type::eTypeDictionary) { + if (breadcrumb && breadcrumb->GetType() == eStructuredDataTypeDictionary) { if (printed_activity) strm.Printf("\n"); StructuredData::Dictionary *breadcrumb_dict = breadcrumb->GetAsDictionary(); StructuredData::ObjectSP breadcrumb_text = breadcrumb_dict->GetValueForKey("name"); if (breadcrumb_text && - breadcrumb_text->GetType() == StructuredData::Type::eTypeString) { + breadcrumb_text->GetType() == eStructuredDataTypeString) { strm.Format(" Current Breadcrumb: {0}\n", breadcrumb_text->GetAsString()->GetValue()); } printed_breadcrumb = true; } - if (messages && messages->GetType() == StructuredData::Type::eTypeArray) { + if (messages && messages->GetType() == eStructuredDataTypeArray) { if (printed_breadcrumb) strm.Printf("\n"); StructuredData::Array *messages_array = messages->GetAsArray(); const size_t msg_count = messages_array->GetSize(); if (msg_count > 0) { strm.Printf(" %zu trace messages:\n", msg_count); for (size_t i = 0; i < msg_count; i++) { StructuredData::ObjectSP message = messages_array->GetItemAtIndex(i); - if (message && - message->GetType() == StructuredData::Type::eTypeDictionary) { + if (message && message->GetType() == eStructuredDataTypeDictionary) { StructuredData::Dictionary *message_dict = message->GetAsDictionary(); StructuredData::ObjectSP message_text = message_dict->GetValueForKey("message"); if (message_text && - message_text->GetType() == StructuredData::Type::eTypeString) { + message_text->GetType() == eStructuredDataTypeString) { strm.Format(" {0}\n", message_text->GetAsString()->GetValue()); } } } } } } return true; } size_t Thread::GetStackFrameStatus(Stream &strm, uint32_t first_frame, uint32_t num_frames, bool show_frame_info, uint32_t num_frames_with_source) { return GetStackFrameList()->GetStatus( strm, first_frame, num_frames, show_frame_info, num_frames_with_source); } Unwind *Thread::GetUnwinder() { if (!m_unwinder_ap) { const ArchSpec target_arch(CalculateTarget()->GetArchitecture()); const llvm::Triple::ArchType machine = target_arch.GetMachine(); switch (machine) { case llvm::Triple::x86_64: case llvm::Triple::x86: case llvm::Triple::arm: case llvm::Triple::aarch64: case llvm::Triple::thumb: case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: case llvm::Triple::mips64el: case llvm::Triple::ppc: case llvm::Triple::ppc64: case llvm::Triple::systemz: case llvm::Triple::hexagon: m_unwinder_ap.reset(new UnwindLLDB(*this)); break; default: if (target_arch.GetTriple().getVendor() == llvm::Triple::Apple) m_unwinder_ap.reset(new UnwindMacOSXFrameBackchain(*this)); break; } } return m_unwinder_ap.get(); } void Thread::Flush() { ClearStackFrames(); m_reg_context_sp.reset(); } bool Thread::IsStillAtLastBreakpointHit() { // If we are currently stopped at a breakpoint, always return that stopinfo // and don't reset it. // This allows threads to maintain their breakpoint stopinfo, such as when // thread-stepping in // multithreaded programs. if (m_stop_info_sp) { StopReason stop_reason = m_stop_info_sp->GetStopReason(); if (stop_reason == lldb::eStopReasonBreakpoint) { uint64_t value = m_stop_info_sp->GetValue(); lldb::RegisterContextSP reg_ctx_sp(GetRegisterContext()); if (reg_ctx_sp) { lldb::addr_t pc = reg_ctx_sp->GetPC(); BreakpointSiteSP bp_site_sp = GetProcess()->GetBreakpointSiteList().FindByAddress(pc); if (bp_site_sp && static_cast(value) == bp_site_sp->GetID()) return true; } } } return false; } Status Thread::StepIn(bool source_step, LazyBool step_in_avoids_code_without_debug_info, LazyBool step_out_avoids_code_without_debug_info) { Status error; Process *process = GetProcess().get(); if (StateIsStoppedState(process->GetState(), true)) { StackFrameSP frame_sp = GetStackFrameAtIndex(0); ThreadPlanSP new_plan_sp; const lldb::RunMode run_mode = eOnlyThisThread; const bool abort_other_plans = false; if (source_step && frame_sp && frame_sp->HasDebugInformation()) { SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); new_plan_sp = QueueThreadPlanForStepInRange( abort_other_plans, sc.line_entry, sc, nullptr, run_mode, step_in_avoids_code_without_debug_info, step_out_avoids_code_without_debug_info); } else { new_plan_sp = QueueThreadPlanForStepSingleInstruction( false, abort_other_plans, run_mode); } new_plan_sp->SetIsMasterPlan(true); new_plan_sp->SetOkayToDiscard(false); // Why do we need to set the current thread by ID here??? process->GetThreadList().SetSelectedThreadByID(GetID()); error = process->Resume(); } else { error.SetErrorString("process not stopped"); } return error; } Status Thread::StepOver(bool source_step, LazyBool step_out_avoids_code_without_debug_info) { Status error; Process *process = GetProcess().get(); if (StateIsStoppedState(process->GetState(), true)) { StackFrameSP frame_sp = GetStackFrameAtIndex(0); ThreadPlanSP new_plan_sp; const lldb::RunMode run_mode = eOnlyThisThread; const bool abort_other_plans = false; if (source_step && frame_sp && frame_sp->HasDebugInformation()) { SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); new_plan_sp = QueueThreadPlanForStepOverRange( abort_other_plans, sc.line_entry, sc, run_mode, step_out_avoids_code_without_debug_info); } else { new_plan_sp = QueueThreadPlanForStepSingleInstruction( true, abort_other_plans, run_mode); } new_plan_sp->SetIsMasterPlan(true); new_plan_sp->SetOkayToDiscard(false); // Why do we need to set the current thread by ID here??? process->GetThreadList().SetSelectedThreadByID(GetID()); error = process->Resume(); } else { error.SetErrorString("process not stopped"); } return error; } Status Thread::StepOut() { Status error; Process *process = GetProcess().get(); if (StateIsStoppedState(process->GetState(), true)) { const bool first_instruction = false; const bool stop_other_threads = false; const bool abort_other_plans = false; ThreadPlanSP new_plan_sp(QueueThreadPlanForStepOut( abort_other_plans, nullptr, first_instruction, stop_other_threads, eVoteYes, eVoteNoOpinion, 0)); new_plan_sp->SetIsMasterPlan(true); new_plan_sp->SetOkayToDiscard(false); // Why do we need to set the current thread by ID here??? process->GetThreadList().SetSelectedThreadByID(GetID()); error = process->Resume(); } else { error.SetErrorString("process not stopped"); } return error; } Index: vendor/lldb/dist/source/Utility/StringExtractor.cpp =================================================================== --- vendor/lldb/dist/source/Utility/StringExtractor.cpp (revision 319149) +++ vendor/lldb/dist/source/Utility/StringExtractor.cpp (revision 319150) @@ -1,418 +1,427 @@ //===-- StringExtractor.cpp -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Utility/StringExtractor.h" #include #include // for isxdigit, isspace #include #include // for memset static inline int xdigit_to_sint(char ch) { if (ch >= 'a' && ch <= 'f') return 10 + ch - 'a'; if (ch >= 'A' && ch <= 'F') return 10 + ch - 'A'; if (ch >= '0' && ch <= '9') return ch - '0'; return -1; } //---------------------------------------------------------------------- // StringExtractor constructor //---------------------------------------------------------------------- StringExtractor::StringExtractor() : m_packet(), m_index(0) {} StringExtractor::StringExtractor(llvm::StringRef packet_str) : m_packet(), m_index(0) { m_packet.assign(packet_str.begin(), packet_str.end()); } StringExtractor::StringExtractor(const char *packet_cstr) : m_packet(), m_index(0) { if (packet_cstr) m_packet.assign(packet_cstr); } //---------------------------------------------------------------------- // StringExtractor copy constructor //---------------------------------------------------------------------- StringExtractor::StringExtractor(const StringExtractor &rhs) : m_packet(rhs.m_packet), m_index(rhs.m_index) {} //---------------------------------------------------------------------- // StringExtractor assignment operator //---------------------------------------------------------------------- const StringExtractor &StringExtractor::operator=(const StringExtractor &rhs) { if (this != &rhs) { m_packet = rhs.m_packet; m_index = rhs.m_index; } return *this; } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- StringExtractor::~StringExtractor() {} char StringExtractor::GetChar(char fail_value) { if (m_index < m_packet.size()) { char ch = m_packet[m_index]; ++m_index; return ch; } m_index = UINT64_MAX; return fail_value; } //---------------------------------------------------------------------- // If a pair of valid hex digits exist at the head of the // StringExtractor they are decoded into an unsigned byte and returned // by this function // // If there is not a pair of valid hex digits at the head of the // StringExtractor, it is left unchanged and -1 is returned //---------------------------------------------------------------------- int StringExtractor::DecodeHexU8() { SkipSpaces(); if (GetBytesLeft() < 2) { return -1; } const int hi_nibble = xdigit_to_sint(m_packet[m_index]); const int lo_nibble = xdigit_to_sint(m_packet[m_index + 1]); if (hi_nibble == -1 || lo_nibble == -1) { return -1; } m_index += 2; return (uint8_t)((hi_nibble << 4) + lo_nibble); } //---------------------------------------------------------------------- // Extract an unsigned character from two hex ASCII chars in the packet // string, or return fail_value on failure //---------------------------------------------------------------------- uint8_t StringExtractor::GetHexU8(uint8_t fail_value, bool set_eof_on_fail) { // On success, fail_value will be overwritten with the next // character in the stream GetHexU8Ex(fail_value, set_eof_on_fail); return fail_value; } bool StringExtractor::GetHexU8Ex(uint8_t &ch, bool set_eof_on_fail) { int byte = DecodeHexU8(); if (byte == -1) { if (set_eof_on_fail || m_index >= m_packet.size()) m_index = UINT64_MAX; // ch should not be changed in case of failure return false; } ch = (uint8_t)byte; return true; } uint32_t StringExtractor::GetU32(uint32_t fail_value, int base) { if (m_index < m_packet.size()) { char *end = nullptr; const char *start = m_packet.c_str(); const char *cstr = start + m_index; uint32_t result = static_cast(::strtoul(cstr, &end, base)); if (end && end != cstr) { m_index = end - start; return result; } } return fail_value; } int32_t StringExtractor::GetS32(int32_t fail_value, int base) { if (m_index < m_packet.size()) { char *end = nullptr; const char *start = m_packet.c_str(); const char *cstr = start + m_index; int32_t result = static_cast(::strtol(cstr, &end, base)); if (end && end != cstr) { m_index = end - start; return result; } } return fail_value; } uint64_t StringExtractor::GetU64(uint64_t fail_value, int base) { if (m_index < m_packet.size()) { char *end = nullptr; const char *start = m_packet.c_str(); const char *cstr = start + m_index; uint64_t result = ::strtoull(cstr, &end, base); if (end && end != cstr) { m_index = end - start; return result; } } return fail_value; } int64_t StringExtractor::GetS64(int64_t fail_value, int base) { if (m_index < m_packet.size()) { char *end = nullptr; const char *start = m_packet.c_str(); const char *cstr = start + m_index; int64_t result = ::strtoll(cstr, &end, base); if (end && end != cstr) { m_index = end - start; return result; } } return fail_value; } uint32_t StringExtractor::GetHexMaxU32(bool little_endian, uint32_t fail_value) { uint32_t result = 0; uint32_t nibble_count = 0; SkipSpaces(); if (little_endian) { uint32_t shift_amount = 0; while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { // Make sure we don't exceed the size of a uint32_t... if (nibble_count >= (sizeof(uint32_t) * 2)) { m_index = UINT64_MAX; return fail_value; } uint8_t nibble_lo; uint8_t nibble_hi = xdigit_to_sint(m_packet[m_index]); ++m_index; if (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { nibble_lo = xdigit_to_sint(m_packet[m_index]); ++m_index; result |= ((uint32_t)nibble_hi << (shift_amount + 4)); result |= ((uint32_t)nibble_lo << shift_amount); nibble_count += 2; shift_amount += 8; } else { result |= ((uint32_t)nibble_hi << shift_amount); nibble_count += 1; shift_amount += 4; } } } else { while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { // Make sure we don't exceed the size of a uint32_t... if (nibble_count >= (sizeof(uint32_t) * 2)) { m_index = UINT64_MAX; return fail_value; } uint8_t nibble = xdigit_to_sint(m_packet[m_index]); // Big Endian result <<= 4; result |= nibble; ++m_index; ++nibble_count; } } return result; } uint64_t StringExtractor::GetHexMaxU64(bool little_endian, uint64_t fail_value) { uint64_t result = 0; uint32_t nibble_count = 0; SkipSpaces(); if (little_endian) { uint32_t shift_amount = 0; while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { // Make sure we don't exceed the size of a uint64_t... if (nibble_count >= (sizeof(uint64_t) * 2)) { m_index = UINT64_MAX; return fail_value; } uint8_t nibble_lo; uint8_t nibble_hi = xdigit_to_sint(m_packet[m_index]); ++m_index; if (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { nibble_lo = xdigit_to_sint(m_packet[m_index]); ++m_index; result |= ((uint64_t)nibble_hi << (shift_amount + 4)); result |= ((uint64_t)nibble_lo << shift_amount); nibble_count += 2; shift_amount += 8; } else { result |= ((uint64_t)nibble_hi << shift_amount); nibble_count += 1; shift_amount += 4; } } } else { while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) { // Make sure we don't exceed the size of a uint64_t... if (nibble_count >= (sizeof(uint64_t) * 2)) { m_index = UINT64_MAX; return fail_value; } uint8_t nibble = xdigit_to_sint(m_packet[m_index]); // Big Endian result <<= 4; result |= nibble; ++m_index; ++nibble_count; } } return result; } +bool StringExtractor::ConsumeFront(const llvm::StringRef &str) { + llvm::StringRef S = GetStringRef(); + if (!S.startswith(str)) + return false; + else + m_index += str.size(); + return true; +} + size_t StringExtractor::GetHexBytes(llvm::MutableArrayRef dest, uint8_t fail_fill_value) { size_t bytes_extracted = 0; while (!dest.empty() && GetBytesLeft() > 0) { dest[0] = GetHexU8(fail_fill_value); if (!IsGood()) break; ++bytes_extracted; dest = dest.drop_front(); } if (!dest.empty()) ::memset(dest.data(), fail_fill_value, dest.size()); return bytes_extracted; } //---------------------------------------------------------------------- // Decodes all valid hex encoded bytes at the head of the // StringExtractor, limited by dst_len. // // Returns the number of bytes successfully decoded //---------------------------------------------------------------------- size_t StringExtractor::GetHexBytesAvail(llvm::MutableArrayRef dest) { size_t bytes_extracted = 0; while (!dest.empty()) { int decode = DecodeHexU8(); if (decode == -1) break; dest[0] = (uint8_t)decode; dest = dest.drop_front(); ++bytes_extracted; } return bytes_extracted; } // Consume ASCII hex nibble character pairs until we have decoded byte_size // bytes of data. uint64_t StringExtractor::GetHexWithFixedSize(uint32_t byte_size, bool little_endian, uint64_t fail_value) { if (byte_size <= 8 && GetBytesLeft() >= byte_size * 2) { uint64_t result = 0; uint32_t i; if (little_endian) { // Little Endian uint32_t shift_amount; for (i = 0, shift_amount = 0; i < byte_size && IsGood(); ++i, shift_amount += 8) { result |= ((uint64_t)GetHexU8() << shift_amount); } } else { // Big Endian for (i = 0; i < byte_size && IsGood(); ++i) { result <<= 8; result |= GetHexU8(); } } } m_index = UINT64_MAX; return fail_value; } size_t StringExtractor::GetHexByteString(std::string &str) { str.clear(); str.reserve(GetBytesLeft() / 2); char ch; while ((ch = GetHexU8()) != '\0') str.append(1, ch); return str.size(); } size_t StringExtractor::GetHexByteStringFixedLength(std::string &str, uint32_t nibble_length) { str.clear(); uint32_t nibble_count = 0; for (const char *pch = Peek(); (nibble_count < nibble_length) && (pch != nullptr); str.append(1, GetHexU8(0, false)), pch = Peek(), nibble_count += 2) { } return str.size(); } size_t StringExtractor::GetHexByteStringTerminatedBy(std::string &str, char terminator) { str.clear(); char ch; while ((ch = GetHexU8(0, false)) != '\0') str.append(1, ch); if (Peek() && *Peek() == terminator) return str.size(); str.clear(); return str.size(); } bool StringExtractor::GetNameColonValue(llvm::StringRef &name, llvm::StringRef &value) { // Read something in the form of NNNN:VVVV; where NNNN is any character // that is not a colon, followed by a ':' character, then a value (one or // more ';' chars), followed by a ';' if (m_index >= m_packet.size()) return fail(); llvm::StringRef view(m_packet); if (view.empty()) return fail(); llvm::StringRef a, b, c, d; view = view.substr(m_index); std::tie(a, b) = view.split(':'); if (a.empty() || b.empty()) return fail(); std::tie(c, d) = b.split(';'); if (b == c && d.empty()) return fail(); name = a; value = c; if (d.empty()) m_index = m_packet.size(); else { size_t bytes_consumed = d.data() - view.data(); m_index += bytes_consumed; } return true; } void StringExtractor::SkipSpaces() { const size_t n = m_packet.size(); while (m_index < n && isspace(m_packet[m_index])) ++m_index; } Index: vendor/lldb/dist/source/Utility/StringExtractorGDBRemote.cpp =================================================================== --- vendor/lldb/dist/source/Utility/StringExtractorGDBRemote.cpp (revision 319149) +++ vendor/lldb/dist/source/Utility/StringExtractorGDBRemote.cpp (revision 319150) @@ -1,568 +1,578 @@ //===-- StringExtractorGDBRemote.cpp ----------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "Utility/StringExtractorGDBRemote.h" #include // for isxdigit #include StringExtractorGDBRemote::ResponseType StringExtractorGDBRemote::GetResponseType() const { if (m_packet.empty()) return eUnsupported; switch (m_packet[0]) { case 'E': if (m_packet.size() == 3 && isxdigit(m_packet[1]) && isxdigit(m_packet[2])) return eError; break; case 'O': if (m_packet.size() == 2 && m_packet[1] == 'K') return eOK; break; case '+': if (m_packet.size() == 1) return eAck; break; case '-': if (m_packet.size() == 1) return eNack; break; } return eResponse; } StringExtractorGDBRemote::ServerPacketType StringExtractorGDBRemote::GetServerPacketType() const { #define PACKET_MATCHES(s) \ ((packet_size == (sizeof(s) - 1)) && (strcmp((packet_cstr), (s)) == 0)) #define PACKET_STARTS_WITH(s) \ ((packet_size >= (sizeof(s) - 1)) && \ ::strncmp(packet_cstr, s, (sizeof(s) - 1)) == 0) // Empty is not a supported packet... if (m_packet.empty()) return eServerPacketType_invalid; const size_t packet_size = m_packet.size(); const char *packet_cstr = m_packet.c_str(); switch (m_packet[0]) { case '%': return eServerPacketType_notify; case '\x03': if (packet_size == 1) return eServerPacketType_interrupt; break; case '-': if (packet_size == 1) return eServerPacketType_nack; break; case '+': if (packet_size == 1) return eServerPacketType_ack; break; case 'A': return eServerPacketType_A; case 'Q': switch (packet_cstr[1]) { case 'E': if (PACKET_STARTS_WITH("QEnvironment:")) return eServerPacketType_QEnvironment; if (PACKET_STARTS_WITH("QEnvironmentHexEncoded:")) return eServerPacketType_QEnvironmentHexEncoded; break; case 'P': if (PACKET_STARTS_WITH("QPassSignals:")) return eServerPacketType_QPassSignals; case 'S': if (PACKET_MATCHES("QStartNoAckMode")) return eServerPacketType_QStartNoAckMode; if (PACKET_STARTS_WITH("QSaveRegisterState")) return eServerPacketType_QSaveRegisterState; if (PACKET_STARTS_WITH("QSetDisableASLR:")) return eServerPacketType_QSetDisableASLR; if (PACKET_STARTS_WITH("QSetDetachOnError:")) return eServerPacketType_QSetDetachOnError; if (PACKET_STARTS_WITH("QSetSTDIN:")) return eServerPacketType_QSetSTDIN; if (PACKET_STARTS_WITH("QSetSTDOUT:")) return eServerPacketType_QSetSTDOUT; if (PACKET_STARTS_WITH("QSetSTDERR:")) return eServerPacketType_QSetSTDERR; if (PACKET_STARTS_WITH("QSetWorkingDir:")) return eServerPacketType_QSetWorkingDir; if (PACKET_STARTS_WITH("QSetLogging:")) return eServerPacketType_QSetLogging; if (PACKET_STARTS_WITH("QSetMaxPacketSize:")) return eServerPacketType_QSetMaxPacketSize; if (PACKET_STARTS_WITH("QSetMaxPayloadSize:")) return eServerPacketType_QSetMaxPayloadSize; if (PACKET_STARTS_WITH("QSetEnableAsyncProfiling;")) return eServerPacketType_QSetEnableAsyncProfiling; if (PACKET_STARTS_WITH("QSyncThreadState:")) return eServerPacketType_QSyncThreadState; break; case 'L': if (PACKET_STARTS_WITH("QLaunchArch:")) return eServerPacketType_QLaunchArch; if (PACKET_MATCHES("QListThreadsInStopReply")) return eServerPacketType_QListThreadsInStopReply; break; case 'R': if (PACKET_STARTS_WITH("QRestoreRegisterState:")) return eServerPacketType_QRestoreRegisterState; break; case 'T': if (PACKET_MATCHES("QThreadSuffixSupported")) return eServerPacketType_QThreadSuffixSupported; break; } break; case 'q': switch (packet_cstr[1]) { case 's': if (PACKET_MATCHES("qsProcessInfo")) return eServerPacketType_qsProcessInfo; if (PACKET_MATCHES("qsThreadInfo")) return eServerPacketType_qsThreadInfo; break; case 'f': if (PACKET_STARTS_WITH("qfProcessInfo")) return eServerPacketType_qfProcessInfo; if (PACKET_STARTS_WITH("qfThreadInfo")) return eServerPacketType_qfThreadInfo; break; case 'C': if (packet_size == 2) return eServerPacketType_qC; break; case 'E': if (PACKET_STARTS_WITH("qEcho:")) return eServerPacketType_qEcho; break; case 'F': if (PACKET_STARTS_WITH("qFileLoadAddress:")) return eServerPacketType_qFileLoadAddress; break; case 'G': if (PACKET_STARTS_WITH("qGroupName:")) return eServerPacketType_qGroupName; if (PACKET_MATCHES("qGetWorkingDir")) return eServerPacketType_qGetWorkingDir; if (PACKET_MATCHES("qGetPid")) return eServerPacketType_qGetPid; if (PACKET_STARTS_WITH("qGetProfileData;")) return eServerPacketType_qGetProfileData; if (PACKET_MATCHES("qGDBServerVersion")) return eServerPacketType_qGDBServerVersion; break; case 'H': if (PACKET_MATCHES("qHostInfo")) return eServerPacketType_qHostInfo; break; case 'K': if (PACKET_STARTS_WITH("qKillSpawnedProcess")) return eServerPacketType_qKillSpawnedProcess; break; case 'L': if (PACKET_STARTS_WITH("qLaunchGDBServer")) return eServerPacketType_qLaunchGDBServer; if (PACKET_MATCHES("qLaunchSuccess")) return eServerPacketType_qLaunchSuccess; break; case 'M': if (PACKET_STARTS_WITH("qMemoryRegionInfo:")) return eServerPacketType_qMemoryRegionInfo; if (PACKET_MATCHES("qMemoryRegionInfo")) return eServerPacketType_qMemoryRegionInfoSupported; if (PACKET_STARTS_WITH("qModuleInfo:")) return eServerPacketType_qModuleInfo; break; case 'P': if (PACKET_STARTS_WITH("qProcessInfoPID:")) return eServerPacketType_qProcessInfoPID; if (PACKET_STARTS_WITH("qPlatform_shell:")) return eServerPacketType_qPlatform_shell; if (PACKET_STARTS_WITH("qPlatform_mkdir:")) return eServerPacketType_qPlatform_mkdir; if (PACKET_STARTS_WITH("qPlatform_chmod:")) return eServerPacketType_qPlatform_chmod; if (PACKET_MATCHES("qProcessInfo")) return eServerPacketType_qProcessInfo; break; case 'Q': if (PACKET_MATCHES("qQueryGDBServer")) return eServerPacketType_qQueryGDBServer; break; case 'R': if (PACKET_STARTS_WITH("qRcmd,")) return eServerPacketType_qRcmd; if (PACKET_STARTS_WITH("qRegisterInfo")) return eServerPacketType_qRegisterInfo; break; case 'S': if (PACKET_STARTS_WITH("qSpeedTest:")) return eServerPacketType_qSpeedTest; if (PACKET_MATCHES("qShlibInfoAddr")) return eServerPacketType_qShlibInfoAddr; if (PACKET_MATCHES("qStepPacketSupported")) return eServerPacketType_qStepPacketSupported; if (PACKET_STARTS_WITH("qSupported")) return eServerPacketType_qSupported; if (PACKET_MATCHES("qSyncThreadStateSupported")) return eServerPacketType_qSyncThreadStateSupported; break; case 'T': if (PACKET_STARTS_WITH("qThreadExtraInfo,")) return eServerPacketType_qThreadExtraInfo; if (PACKET_STARTS_WITH("qThreadStopInfo")) return eServerPacketType_qThreadStopInfo; break; case 'U': if (PACKET_STARTS_WITH("qUserName:")) return eServerPacketType_qUserName; break; case 'V': if (PACKET_MATCHES("qVAttachOrWaitSupported")) return eServerPacketType_qVAttachOrWaitSupported; break; case 'W': if (PACKET_STARTS_WITH("qWatchpointSupportInfo:")) return eServerPacketType_qWatchpointSupportInfo; if (PACKET_MATCHES("qWatchpointSupportInfo")) return eServerPacketType_qWatchpointSupportInfoSupported; break; case 'X': if (PACKET_STARTS_WITH("qXfer:auxv:read::")) return eServerPacketType_qXfer_auxv_read; break; } break; case 'j': if (PACKET_STARTS_WITH("jModulesInfo:")) return eServerPacketType_jModulesInfo; if (PACKET_MATCHES("jSignalsInfo")) return eServerPacketType_jSignalsInfo; if (PACKET_MATCHES("jThreadsInfo")) return eServerPacketType_jThreadsInfo; + if (PACKET_STARTS_WITH("jTraceBufferRead:")) + return eServerPacketType_jTraceBufferRead; + if (PACKET_STARTS_WITH("jTraceConfigRead:")) + return eServerPacketType_jTraceConfigRead; + if (PACKET_STARTS_WITH("jTraceMetaRead:")) + return eServerPacketType_jTraceMetaRead; + if (PACKET_STARTS_WITH("jTraceStart:")) + return eServerPacketType_jTraceStart; + if (PACKET_STARTS_WITH("jTraceStop:")) + return eServerPacketType_jTraceStop; break; case 'v': if (PACKET_STARTS_WITH("vFile:")) { if (PACKET_STARTS_WITH("vFile:open:")) return eServerPacketType_vFile_open; else if (PACKET_STARTS_WITH("vFile:close:")) return eServerPacketType_vFile_close; else if (PACKET_STARTS_WITH("vFile:pread")) return eServerPacketType_vFile_pread; else if (PACKET_STARTS_WITH("vFile:pwrite")) return eServerPacketType_vFile_pwrite; else if (PACKET_STARTS_WITH("vFile:size")) return eServerPacketType_vFile_size; else if (PACKET_STARTS_WITH("vFile:exists")) return eServerPacketType_vFile_exists; else if (PACKET_STARTS_WITH("vFile:stat")) return eServerPacketType_vFile_stat; else if (PACKET_STARTS_WITH("vFile:mode")) return eServerPacketType_vFile_mode; else if (PACKET_STARTS_WITH("vFile:MD5")) return eServerPacketType_vFile_md5; else if (PACKET_STARTS_WITH("vFile:symlink")) return eServerPacketType_vFile_symlink; else if (PACKET_STARTS_WITH("vFile:unlink")) return eServerPacketType_vFile_unlink; } else { if (PACKET_STARTS_WITH("vAttach;")) return eServerPacketType_vAttach; if (PACKET_STARTS_WITH("vAttachWait;")) return eServerPacketType_vAttachWait; if (PACKET_STARTS_WITH("vAttachOrWait;")) return eServerPacketType_vAttachOrWait; if (PACKET_STARTS_WITH("vAttachName;")) return eServerPacketType_vAttachName; if (PACKET_STARTS_WITH("vCont;")) return eServerPacketType_vCont; if (PACKET_MATCHES("vCont?")) return eServerPacketType_vCont_actions; } break; case '_': switch (packet_cstr[1]) { case 'M': return eServerPacketType__M; case 'm': return eServerPacketType__m; } break; case '?': if (packet_size == 1) return eServerPacketType_stop_reason; break; case 'c': return eServerPacketType_c; case 'C': return eServerPacketType_C; case 'D': if (packet_size == 1) return eServerPacketType_D; break; case 'g': if (packet_size == 1) return eServerPacketType_g; break; case 'G': return eServerPacketType_G; case 'H': return eServerPacketType_H; case 'I': return eServerPacketType_I; case 'k': if (packet_size == 1) return eServerPacketType_k; break; case 'm': return eServerPacketType_m; case 'M': return eServerPacketType_M; case 'p': return eServerPacketType_p; case 'P': return eServerPacketType_P; case 's': if (packet_size == 1) return eServerPacketType_s; break; case 'S': return eServerPacketType_S; case 'x': return eServerPacketType_x; case 'X': return eServerPacketType_X; case 'T': return eServerPacketType_T; case 'z': if (packet_cstr[1] >= '0' && packet_cstr[1] <= '4') return eServerPacketType_z; break; case 'Z': if (packet_cstr[1] >= '0' && packet_cstr[1] <= '4') return eServerPacketType_Z; break; } return eServerPacketType_unimplemented; } bool StringExtractorGDBRemote::IsOKResponse() const { return GetResponseType() == eOK; } bool StringExtractorGDBRemote::IsUnsupportedResponse() const { return GetResponseType() == eUnsupported; } bool StringExtractorGDBRemote::IsNormalResponse() const { return GetResponseType() == eResponse; } bool StringExtractorGDBRemote::IsErrorResponse() const { return GetResponseType() == eError && m_packet.size() == 3 && isxdigit(m_packet[1]) && isxdigit(m_packet[2]); } uint8_t StringExtractorGDBRemote::GetError() { if (GetResponseType() == eError) { SetFilePos(1); return GetHexU8(255); } return 0; } size_t StringExtractorGDBRemote::GetEscapedBinaryData(std::string &str) { // Just get the data bytes in the string as // GDBRemoteCommunication::CheckForPacket() // already removes any 0x7d escaped characters. If any 0x7d characters are // left in // the packet, then they are supposed to be there... str.clear(); const size_t bytes_left = GetBytesLeft(); if (bytes_left > 0) { str.assign(m_packet, m_index, bytes_left); m_index += bytes_left; } return str.size(); } static bool OKErrorNotSupportedResponseValidator(void *, const StringExtractorGDBRemote &response) { switch (response.GetResponseType()) { case StringExtractorGDBRemote::eOK: case StringExtractorGDBRemote::eError: case StringExtractorGDBRemote::eUnsupported: return true; case StringExtractorGDBRemote::eAck: case StringExtractorGDBRemote::eNack: case StringExtractorGDBRemote::eResponse: break; } return false; } static bool JSONResponseValidator(void *, const StringExtractorGDBRemote &response) { switch (response.GetResponseType()) { case StringExtractorGDBRemote::eUnsupported: case StringExtractorGDBRemote::eError: return true; // Accept unsupported or EXX as valid responses case StringExtractorGDBRemote::eOK: case StringExtractorGDBRemote::eAck: case StringExtractorGDBRemote::eNack: break; case StringExtractorGDBRemote::eResponse: // JSON that is returned in from JSON query packets is currently always // either a dictionary which starts with a '{', or an array which // starts with a '['. This is a quick validator to just make sure the // response could be valid JSON without having to validate all of the // JSON content. switch (response.GetStringRef()[0]) { case '{': return true; case '[': return true; default: break; } break; } return false; } static bool ASCIIHexBytesResponseValidator(void *, const StringExtractorGDBRemote &response) { switch (response.GetResponseType()) { case StringExtractorGDBRemote::eUnsupported: case StringExtractorGDBRemote::eError: return true; // Accept unsupported or EXX as valid responses case StringExtractorGDBRemote::eOK: case StringExtractorGDBRemote::eAck: case StringExtractorGDBRemote::eNack: break; case StringExtractorGDBRemote::eResponse: { uint32_t valid_count = 0; for (const char ch : response.GetStringRef()) { if (!isxdigit(ch)) { return false; } if (++valid_count >= 16) break; // Don't validate all the characters in case the packet is very // large } return true; } break; } return false; } void StringExtractorGDBRemote::CopyResponseValidator( const StringExtractorGDBRemote &rhs) { m_validator = rhs.m_validator; m_validator_baton = rhs.m_validator_baton; } void StringExtractorGDBRemote::SetResponseValidator( ResponseValidatorCallback callback, void *baton) { m_validator = callback; m_validator_baton = baton; } void StringExtractorGDBRemote::SetResponseValidatorToOKErrorNotSupported() { m_validator = OKErrorNotSupportedResponseValidator; m_validator_baton = nullptr; } void StringExtractorGDBRemote::SetResponseValidatorToASCIIHexBytes() { m_validator = ASCIIHexBytesResponseValidator; m_validator_baton = nullptr; } void StringExtractorGDBRemote::SetResponseValidatorToJSON() { m_validator = JSONResponseValidator; m_validator_baton = nullptr; } bool StringExtractorGDBRemote::ValidateResponse() const { // If we have a validator callback, try to validate the callback if (m_validator) return m_validator(m_validator_baton, *this); else return true; // No validator, so response is valid } Index: vendor/lldb/dist/source/Utility/StringExtractorGDBRemote.h =================================================================== --- vendor/lldb/dist/source/Utility/StringExtractorGDBRemote.h (revision 319149) +++ vendor/lldb/dist/source/Utility/StringExtractorGDBRemote.h (revision 319150) @@ -1,194 +1,200 @@ //===-- StringExtractorGDBRemote.h ------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef utility_StringExtractorGDBRemote_h_ #define utility_StringExtractorGDBRemote_h_ #include "lldb/Utility/StringExtractor.h" #include "llvm/ADT/StringRef.h" // for StringRef #include #include // for size_t #include // for uint8_t class StringExtractorGDBRemote : public StringExtractor { public: typedef bool (*ResponseValidatorCallback)( void *baton, const StringExtractorGDBRemote &response); StringExtractorGDBRemote() : StringExtractor(), m_validator(nullptr) {} StringExtractorGDBRemote(llvm::StringRef str) : StringExtractor(str), m_validator(nullptr) {} StringExtractorGDBRemote(const char *cstr) : StringExtractor(cstr), m_validator(nullptr) {} StringExtractorGDBRemote(const StringExtractorGDBRemote &rhs) : StringExtractor(rhs), m_validator(rhs.m_validator) {} virtual ~StringExtractorGDBRemote() {} bool ValidateResponse() const; void CopyResponseValidator(const StringExtractorGDBRemote &rhs); void SetResponseValidator(ResponseValidatorCallback callback, void *baton); void SetResponseValidatorToOKErrorNotSupported(); void SetResponseValidatorToASCIIHexBytes(); void SetResponseValidatorToJSON(); enum ServerPacketType { eServerPacketType_nack = 0, eServerPacketType_ack, eServerPacketType_invalid, eServerPacketType_unimplemented, eServerPacketType_interrupt, // CTRL+c packet or "\x03" eServerPacketType_A, // Program arguments packet eServerPacketType_qfProcessInfo, eServerPacketType_qsProcessInfo, eServerPacketType_qC, eServerPacketType_qEcho, eServerPacketType_qGroupName, eServerPacketType_qHostInfo, eServerPacketType_qLaunchGDBServer, eServerPacketType_qQueryGDBServer, eServerPacketType_qKillSpawnedProcess, eServerPacketType_qLaunchSuccess, eServerPacketType_qModuleInfo, eServerPacketType_qProcessInfoPID, eServerPacketType_qSpeedTest, eServerPacketType_qUserName, eServerPacketType_qGetWorkingDir, eServerPacketType_qFileLoadAddress, eServerPacketType_QEnvironment, eServerPacketType_QLaunchArch, eServerPacketType_QSetDisableASLR, eServerPacketType_QSetDetachOnError, eServerPacketType_QSetSTDIN, eServerPacketType_QSetSTDOUT, eServerPacketType_QSetSTDERR, eServerPacketType_QSetWorkingDir, eServerPacketType_QStartNoAckMode, eServerPacketType_qPlatform_shell, eServerPacketType_qPlatform_mkdir, eServerPacketType_qPlatform_chmod, eServerPacketType_vFile_open, eServerPacketType_vFile_close, eServerPacketType_vFile_pread, eServerPacketType_vFile_pwrite, eServerPacketType_vFile_size, eServerPacketType_vFile_mode, eServerPacketType_vFile_exists, eServerPacketType_vFile_md5, eServerPacketType_vFile_stat, eServerPacketType_vFile_symlink, eServerPacketType_vFile_unlink, // debug server packages eServerPacketType_QEnvironmentHexEncoded, eServerPacketType_QListThreadsInStopReply, eServerPacketType_QPassSignals, eServerPacketType_QRestoreRegisterState, eServerPacketType_QSaveRegisterState, eServerPacketType_QSetLogging, eServerPacketType_QSetMaxPacketSize, eServerPacketType_QSetMaxPayloadSize, eServerPacketType_QSetEnableAsyncProfiling, eServerPacketType_QSyncThreadState, eServerPacketType_QThreadSuffixSupported, eServerPacketType_jThreadsInfo, eServerPacketType_qsThreadInfo, eServerPacketType_qfThreadInfo, eServerPacketType_qGetPid, eServerPacketType_qGetProfileData, eServerPacketType_qGDBServerVersion, eServerPacketType_qMemoryRegionInfo, eServerPacketType_qMemoryRegionInfoSupported, eServerPacketType_qProcessInfo, eServerPacketType_qRcmd, eServerPacketType_qRegisterInfo, eServerPacketType_qShlibInfoAddr, eServerPacketType_qStepPacketSupported, eServerPacketType_qSupported, eServerPacketType_qSyncThreadStateSupported, eServerPacketType_qThreadExtraInfo, eServerPacketType_qThreadStopInfo, eServerPacketType_qVAttachOrWaitSupported, eServerPacketType_qWatchpointSupportInfo, eServerPacketType_qWatchpointSupportInfoSupported, eServerPacketType_qXfer_auxv_read, eServerPacketType_jSignalsInfo, eServerPacketType_jModulesInfo, eServerPacketType_vAttach, eServerPacketType_vAttachWait, eServerPacketType_vAttachOrWait, eServerPacketType_vAttachName, eServerPacketType_vCont, eServerPacketType_vCont_actions, // vCont? eServerPacketType_stop_reason, // '?' eServerPacketType_c, eServerPacketType_C, eServerPacketType_D, eServerPacketType_g, eServerPacketType_G, eServerPacketType_H, eServerPacketType_I, // stdin notification eServerPacketType_k, eServerPacketType_m, eServerPacketType_M, eServerPacketType_p, eServerPacketType_P, eServerPacketType_s, eServerPacketType_S, eServerPacketType_T, eServerPacketType_x, eServerPacketType_X, eServerPacketType_Z, eServerPacketType_z, eServerPacketType__M, eServerPacketType__m, eServerPacketType_notify, // '%' notification + + eServerPacketType_jTraceStart, + eServerPacketType_jTraceBufferRead, + eServerPacketType_jTraceMetaRead, + eServerPacketType_jTraceStop, + eServerPacketType_jTraceConfigRead, }; ServerPacketType GetServerPacketType() const; enum ResponseType { eUnsupported = 0, eAck, eNack, eError, eOK, eResponse }; ResponseType GetResponseType() const; bool IsOKResponse() const; bool IsUnsupportedResponse() const; bool IsNormalResponse() const; bool IsErrorResponse() const; // Returns zero if the packet isn't a EXX packet where XX are two hex // digits. Otherwise the error encoded in XX is returned. uint8_t GetError(); size_t GetEscapedBinaryData(std::string &str); protected: ResponseValidatorCallback m_validator; void *m_validator_baton; }; #endif // utility_StringExtractorGDBRemote_h_ Index: vendor/lldb/dist/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp =================================================================== --- vendor/lldb/dist/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp (revision 319149) +++ vendor/lldb/dist/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp (revision 319150) @@ -1,372 +1,598 @@ //===-- GDBRemoteCommunicationClientTest.cpp --------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include "GDBRemoteTestUtils.h" #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h" +#include "lldb/lldb-enumerations.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/StructuredData.h" +#include "lldb/Core/TraceOptions.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Utility/DataBuffer.h" #include "llvm/ADT/ArrayRef.h" using namespace lldb_private::process_gdb_remote; using namespace lldb_private; using namespace lldb; using namespace llvm; namespace { typedef GDBRemoteCommunication::PacketResult PacketResult; struct TestClient : public GDBRemoteCommunicationClient { TestClient() { m_send_acks = false; } }; void Handle_QThreadSuffixSupported(MockServer &server, bool supported) { StringExtractorGDBRemote request; ASSERT_EQ(PacketResult::Success, server.GetPacket(request)); ASSERT_EQ("QThreadSuffixSupported", request.GetStringRef()); if (supported) ASSERT_EQ(PacketResult::Success, server.SendOKResponse()); else ASSERT_EQ(PacketResult::Success, server.SendUnimplementedResponse(nullptr)); } void HandlePacket(MockServer &server, StringRef expected, StringRef response) { StringExtractorGDBRemote request; ASSERT_EQ(PacketResult::Success, server.GetPacket(request)); ASSERT_EQ(expected, request.GetStringRef()); ASSERT_EQ(PacketResult::Success, server.SendPacket(response)); } uint8_t all_registers[] = {'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'}; std::string all_registers_hex = "404142434445464748494a4b4c4d4e4f"; uint8_t one_register[] = {'A', 'B', 'C', 'D'}; std::string one_register_hex = "41424344"; } // end anonymous namespace class GDBRemoteCommunicationClientTest : public GDBRemoteTest {}; TEST_F(GDBRemoteCommunicationClientTest, WriteRegister) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::tid_t tid = 0x47; const uint32_t reg_num = 4; std::future write_result = std::async(std::launch::async, [&] { return client.WriteRegister(tid, reg_num, one_register); }); Handle_QThreadSuffixSupported(server, true); HandlePacket(server, "P4=" + one_register_hex + ";thread:0047;", "OK"); ASSERT_TRUE(write_result.get()); write_result = std::async(std::launch::async, [&] { return client.WriteAllRegisters(tid, all_registers); }); HandlePacket(server, "G" + all_registers_hex + ";thread:0047;", "OK"); ASSERT_TRUE(write_result.get()); } TEST_F(GDBRemoteCommunicationClientTest, WriteRegisterNoSuffix) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::tid_t tid = 0x47; const uint32_t reg_num = 4; std::future write_result = std::async(std::launch::async, [&] { return client.WriteRegister(tid, reg_num, one_register); }); Handle_QThreadSuffixSupported(server, false); HandlePacket(server, "Hg47", "OK"); HandlePacket(server, "P4=" + one_register_hex, "OK"); ASSERT_TRUE(write_result.get()); write_result = std::async(std::launch::async, [&] { return client.WriteAllRegisters(tid, all_registers); }); HandlePacket(server, "G" + all_registers_hex, "OK"); ASSERT_TRUE(write_result.get()); } TEST_F(GDBRemoteCommunicationClientTest, ReadRegister) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::tid_t tid = 0x47; const uint32_t reg_num = 4; std::future async_result = std::async( std::launch::async, [&] { return client.GetpPacketSupported(tid); }); Handle_QThreadSuffixSupported(server, true); HandlePacket(server, "p0;thread:0047;", one_register_hex); ASSERT_TRUE(async_result.get()); std::future read_result = std::async( std::launch::async, [&] { return client.ReadRegister(tid, reg_num); }); HandlePacket(server, "p4;thread:0047;", "41424344"); auto buffer_sp = read_result.get(); ASSERT_TRUE(bool(buffer_sp)); ASSERT_EQ(0, memcmp(buffer_sp->GetBytes(), one_register, sizeof one_register)); read_result = std::async(std::launch::async, [&] { return client.ReadAllRegisters(tid); }); HandlePacket(server, "g;thread:0047;", all_registers_hex); buffer_sp = read_result.get(); ASSERT_TRUE(bool(buffer_sp)); ASSERT_EQ(0, memcmp(buffer_sp->GetBytes(), all_registers, sizeof all_registers)); } TEST_F(GDBRemoteCommunicationClientTest, SaveRestoreRegistersNoSuffix) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::tid_t tid = 0x47; uint32_t save_id; std::future async_result = std::async(std::launch::async, [&] { return client.SaveRegisterState(tid, save_id); }); Handle_QThreadSuffixSupported(server, false); HandlePacket(server, "Hg47", "OK"); HandlePacket(server, "QSaveRegisterState", "1"); ASSERT_TRUE(async_result.get()); EXPECT_EQ(1u, save_id); async_result = std::async(std::launch::async, [&] { return client.RestoreRegisterState(tid, save_id); }); HandlePacket(server, "QRestoreRegisterState:1", "OK"); ASSERT_TRUE(async_result.get()); } TEST_F(GDBRemoteCommunicationClientTest, SyncThreadState) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::tid_t tid = 0x47; std::future async_result = std::async( std::launch::async, [&] { return client.SyncThreadState(tid); }); HandlePacket(server, "qSyncThreadStateSupported", "OK"); HandlePacket(server, "QSyncThreadState:0047;", "OK"); ASSERT_TRUE(async_result.get()); } TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfo) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; llvm::Triple triple("i386-pc-linux"); FileSpec file_specs[] = { FileSpec("/foo/bar.so", false, FileSpec::ePathSyntaxPosix), FileSpec("/foo/baz.so", false, FileSpec::ePathSyntaxPosix), // This is a bit dodgy but we currently depend on GetModulesInfo not // performing denormalization. It can go away once the users // (DynamicLoaderPOSIXDYLD, at least) correctly set the path syntax for // the FileSpecs they create. FileSpec("/foo/baw.so", false, FileSpec::ePathSyntaxWindows), }; std::future>> async_result = std::async(std::launch::async, [&] { return client.GetModulesInfo(file_specs, triple); }); HandlePacket( server, "jModulesInfo:[" R"({"file":"/foo/bar.so","triple":"i386-pc-linux"},)" R"({"file":"/foo/baz.so","triple":"i386-pc-linux"},)" R"({"file":"/foo/baw.so","triple":"i386-pc-linux"}])", R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])"); auto result = async_result.get(); ASSERT_TRUE(result.hasValue()); ASSERT_EQ(1u, result->size()); EXPECT_EQ("/foo/bar.so", result.getValue()[0].GetFileSpec().GetPath()); EXPECT_EQ(triple, result.getValue()[0].GetArchitecture().GetTriple()); EXPECT_EQ(UUID("@ABCDEFGHIJKLMNO", 16), result.getValue()[0].GetUUID()); EXPECT_EQ(0u, result.getValue()[0].GetObjectOffset()); EXPECT_EQ(1234u, result.getValue()[0].GetObjectSize()); } TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfoInvalidResponse) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; llvm::Triple triple("i386-pc-linux"); FileSpec file_spec("/foo/bar.so", false, FileSpec::ePathSyntaxPosix); const char *invalid_responses[] = { "OK", "E47", "[]", // no UUID R"([{"triple":"i386-pc-linux",)" R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}])", // no triple R"([{"uuid":"404142434445464748494a4b4c4d4e4f",)" R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}])", // no file_path R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" R"("file_offset":0,"file_size":1234}])", // no file_offset R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" R"("file_path":"/foo/bar.so","file_size":1234}])", // no file_size R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" R"("file_path":"/foo/bar.so","file_offset":0}])", }; for (const char *response : invalid_responses) { std::future>> async_result = std::async(std::launch::async, [&] { return client.GetModulesInfo(file_spec, triple); }); HandlePacket( server, R"(jModulesInfo:[{"file":"/foo/bar.so","triple":"i386-pc-linux"}])", response); ASSERT_FALSE(async_result.get().hasValue()) << "response was: " << response; } } TEST_F(GDBRemoteCommunicationClientTest, TestPacketSpeedJSON) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; std::thread server_thread([&server] { for (;;) { StringExtractorGDBRemote request; PacketResult result = server.GetPacket(request); if (result == PacketResult::ErrorDisconnected) return; ASSERT_EQ(PacketResult::Success, result); StringRef ref = request.GetStringRef(); ASSERT_TRUE(ref.consume_front("qSpeedTest:response_size:")); int size; ASSERT_FALSE(ref.consumeInteger(10, size)) << "ref: " << ref; std::string response(size, 'X'); ASSERT_EQ(PacketResult::Success, server.SendPacket(response)); } }); StreamString ss; client.TestPacketSpeed(10, 32, 32, 4096, true, ss); client.Disconnect(); server_thread.join(); GTEST_LOG_(INFO) << "Formatted output: " << ss.GetData(); auto object_sp = StructuredData::ParseJSON(ss.GetString()); ASSERT_TRUE(bool(object_sp)); auto dict_sp = object_sp->GetAsDictionary(); ASSERT_TRUE(bool(dict_sp)); object_sp = dict_sp->GetValueForKey("packet_speeds"); ASSERT_TRUE(bool(object_sp)); dict_sp = object_sp->GetAsDictionary(); ASSERT_TRUE(bool(dict_sp)); int num_packets; ASSERT_TRUE(dict_sp->GetValueForKeyAsInteger("num_packets", num_packets)) << ss.GetString(); ASSERT_EQ(10, num_packets); } TEST_F(GDBRemoteCommunicationClientTest, SendSignalsToIgnore) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::tid_t tid = 0x47; const uint32_t reg_num = 4; std::future result = std::async(std::launch::async, [&] { return client.SendSignalsToIgnore({2, 3, 5, 7, 0xB, 0xD, 0x11}); }); HandlePacket(server, "QPassSignals:02;03;05;07;0b;0d;11", "OK"); EXPECT_TRUE(result.get().Success()); result = std::async(std::launch::async, [&] { return client.SendSignalsToIgnore(std::vector()); }); HandlePacket(server, "QPassSignals:", "OK"); EXPECT_TRUE(result.get().Success()); } TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::addr_t addr = 0xa000; MemoryRegionInfo region_info; std::future result = std::async(std::launch::async, [&] { return client.GetMemoryRegionInfo(addr, region_info); }); // name is: /foo/bar.so HandlePacket(server, "qMemoryRegionInfo:a000", "start:a000;size:2000;permissions:rx;name:2f666f6f2f6261722e736f;"); EXPECT_TRUE(result.get().Success()); } TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfoInvalidResponse) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::addr_t addr = 0x4000; MemoryRegionInfo region_info; std::future result = std::async(std::launch::async, [&] { return client.GetMemoryRegionInfo(addr, region_info); }); HandlePacket(server, "qMemoryRegionInfo:4000", "start:4000;size:0000;"); EXPECT_FALSE(result.get().Success()); +} + +TEST_F(GDBRemoteCommunicationClientTest, SendStartTracePacket) { + TestClient client; + MockServer server; + Connect(client, server); + if (HasFailure()) + return; + + TraceOptions options; + Status error; + + options.setType(lldb::TraceType::eTraceTypeProcessorTrace); + options.setMetaDataBufferSize(8192); + options.setTraceBufferSize(8192); + options.setThreadID(0x23); + + StructuredData::DictionarySP custom_params = + std::make_shared(); + custom_params->AddStringItem("tracetech", "intel-pt"); + custom_params->AddIntegerItem("psb", 0x01); + + options.setTraceParams(custom_params); + + std::future result = std::async(std::launch::async, [&] { + return client.SendStartTracePacket(options, error); + }); + + // Since the line is exceeding 80 characters. + std::string expected_packet1 = + R"(jTraceStart:{"buffersize" : 8192,"metabuffersize" : 8192,"params" :)"; + std::string expected_packet2 = + R"( {"psb" : 1,"tracetech" : "intel-pt"},"threadid" : 35,"type" : 1})"; + HandlePacket(server, (expected_packet1 + expected_packet2), "1"); + ASSERT_TRUE(error.Success()); + ASSERT_EQ(result.get(), 1); + + error.Clear(); + result = std::async(std::launch::async, [&] { + return client.SendStartTracePacket(options, error); + }); + + HandlePacket(server, (expected_packet1 + expected_packet2), "E23"); + ASSERT_EQ(result.get(), LLDB_INVALID_UID); + ASSERT_FALSE(error.Success()); +} + +TEST_F(GDBRemoteCommunicationClientTest, SendStopTracePacket) { + TestClient client; + MockServer server; + Connect(client, server); + if (HasFailure()) + return; + + lldb::tid_t thread_id = 0x23; + lldb::user_id_t trace_id = 3; + + std::future result = std::async(std::launch::async, [&] { + return client.SendStopTracePacket(trace_id, thread_id); + }); + + const char *expected_packet = + R"(jTraceStop:{"threadid" : 35,"traceid" : 3})"; + HandlePacket(server, expected_packet, "OK"); + ASSERT_TRUE(result.get().Success()); + + result = std::async(std::launch::async, [&] { + return client.SendStopTracePacket(trace_id, thread_id); + }); + + HandlePacket(server, expected_packet, "E23"); + ASSERT_FALSE(result.get().Success()); +} + +TEST_F(GDBRemoteCommunicationClientTest, SendGetDataPacket) { + TestClient client; + MockServer server; + Connect(client, server); + if (HasFailure()) + return; + + lldb::tid_t thread_id = 0x23; + lldb::user_id_t trace_id = 3; + + uint8_t buf[32] = {}; + llvm::MutableArrayRef buffer(buf, 32); + size_t offset = 0; + + std::future result = std::async(std::launch::async, [&] { + return client.SendGetDataPacket(trace_id, thread_id, buffer, offset); + }); + + std::string expected_packet1 = + R"(jTraceBufferRead:{"buffersize" : 32,"offset" : 0,"threadid" : 35,)"; + std::string expected_packet2 = R"("traceid" : 3})"; + HandlePacket(server, expected_packet1+expected_packet2, "123456"); + ASSERT_TRUE(result.get().Success()); + ASSERT_EQ(buffer.size(), 3); + ASSERT_EQ(buf[0], 0x12); + ASSERT_EQ(buf[1], 0x34); + ASSERT_EQ(buf[2], 0x56); + + llvm::MutableArrayRef buffer2(buf, 32); + result = std::async(std::launch::async, [&] { + return client.SendGetDataPacket(trace_id, thread_id, buffer2, offset); + }); + + HandlePacket(server, expected_packet1+expected_packet2, "E23"); + ASSERT_FALSE(result.get().Success()); + ASSERT_EQ(buffer2.size(), 0); +} + +TEST_F(GDBRemoteCommunicationClientTest, SendGetMetaDataPacket) { + TestClient client; + MockServer server; + Connect(client, server); + if (HasFailure()) + return; + + lldb::tid_t thread_id = 0x23; + lldb::user_id_t trace_id = 3; + + uint8_t buf[32] = {}; + llvm::MutableArrayRef buffer(buf, 32); + size_t offset = 0; + + std::future result = std::async(std::launch::async, [&] { + return client.SendGetMetaDataPacket(trace_id, thread_id, buffer, offset); + }); + + std::string expected_packet1 = + R"(jTraceMetaRead:{"buffersize" : 32,"offset" : 0,"threadid" : 35,)"; + std::string expected_packet2 = R"("traceid" : 3})"; + HandlePacket(server, expected_packet1+expected_packet2, "123456"); + ASSERT_TRUE(result.get().Success()); + ASSERT_EQ(buffer.size(), 3); + ASSERT_EQ(buf[0], 0x12); + ASSERT_EQ(buf[1], 0x34); + ASSERT_EQ(buf[2], 0x56); + + llvm::MutableArrayRef buffer2(buf, 32); + result = std::async(std::launch::async, [&] { + return client.SendGetMetaDataPacket(trace_id, thread_id, buffer2, offset); + }); + + HandlePacket(server, expected_packet1+expected_packet2, "E23"); + ASSERT_FALSE(result.get().Success()); + ASSERT_EQ(buffer2.size(), 0); +} + +TEST_F(GDBRemoteCommunicationClientTest, SendGetTraceConfigPacket) { + TestClient client; + MockServer server; + Connect(client, server); + if (HasFailure()) + return; + + lldb::tid_t thread_id = 0x23; + lldb::user_id_t trace_id = 3; + TraceOptions options; + options.setThreadID(thread_id); + + std::future result = std::async(std::launch::async, [&] { + return client.SendGetTraceConfigPacket(trace_id, options); + }); + + const char *expected_packet = + R"(jTraceConfigRead:{"threadid" : 35,"traceid" : 3})"; + std::string response1 = + R"({"buffersize" : 8192,"params" : {"psb" : 1,"tracetech" : "intel-pt"})"; + std::string response2 = + R"(],"metabuffersize" : 8192,"threadid" : 35,"type" : 1}])"; + HandlePacket(server, expected_packet, response1+response2); + ASSERT_TRUE(result.get().Success()); + ASSERT_EQ(options.getTraceBufferSize(), 8192); + ASSERT_EQ(options.getMetaDataBufferSize(), 8192); + ASSERT_EQ(options.getType(), 1); + + auto custom_params = options.getTraceParams(); + + uint64_t psb_value; + llvm::StringRef trace_tech_value; + + ASSERT_TRUE(custom_params); + ASSERT_EQ(custom_params->GetType(), eStructuredDataTypeDictionary); + ASSERT_TRUE( + custom_params->GetValueForKeyAsInteger("psb", psb_value)); + ASSERT_EQ(psb_value, 1); + ASSERT_TRUE( + custom_params->GetValueForKeyAsString("tracetech", trace_tech_value)); + ASSERT_STREQ(trace_tech_value.data(), "intel-pt"); + + // Checking error response. + std::future result2 = std::async(std::launch::async, [&] { + return client.SendGetTraceConfigPacket(trace_id, options); + }); + + HandlePacket(server, expected_packet, "E23"); + ASSERT_FALSE(result2.get().Success()); + + // Wrong JSON as response. + std::future result3 = std::async(std::launch::async, [&] { + return client.SendGetTraceConfigPacket(trace_id, options); + }); + + std::string incorrect_json1 = + R"("buffersize" : 8192,"params" : {"psb" : 1,"tracetech" : "intel-pt"})"; + std::string incorrect_json2 = + R"(],"metabuffersize" : 8192,"threadid" : 35,"type" : 1}])"; + HandlePacket(server, expected_packet, incorrect_json1+incorrect_json2); + ASSERT_FALSE(result3.get().Success()); + + // Wrong JSON as custom_params. + std::future result4 = std::async(std::launch::async, [&] { + return client.SendGetTraceConfigPacket(trace_id, options); + }); + + std::string incorrect_custom_params1 = + R"({"buffersize" : 8192,"params" : "psb" : 1,"tracetech" : "intel-pt"})"; + std::string incorrect_custom_params2 = + R"(],"metabuffersize" : 8192,"threadid" : 35,"type" : 1}])"; + HandlePacket(server, expected_packet, incorrect_custom_params1+ + incorrect_custom_params2); + ASSERT_FALSE(result4.get().Success()); } Index: vendor/lldb/dist/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp =================================================================== --- vendor/lldb/dist/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp (revision 319149) +++ vendor/lldb/dist/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp (revision 319150) @@ -1,599 +1,600 @@ //===-- PythonDataObjectsTests.cpp ------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "Plugins/ScriptInterpreter/Python/lldb-python.h" #include "gtest/gtest.h" #include "Plugins/ScriptInterpreter/Python/PythonDataObjects.h" #include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h" #include "lldb/Host/File.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" +#include "lldb/lldb-enumerations.h" #include "PythonTestSuite.h" using namespace lldb_private; class PythonDataObjectsTest : public PythonTestSuite { public: void SetUp() override { PythonTestSuite::SetUp(); PythonString sys_module("sys"); m_sys_module.Reset(PyRefType::Owned, PyImport_Import(sys_module.get())); m_main_module = PythonModule::MainModule(); m_builtins_module = PythonModule::BuiltinsModule(); } void TearDown() override { m_sys_module.Reset(); m_main_module.Reset(); m_builtins_module.Reset(); PythonTestSuite::TearDown(); } protected: PythonModule m_sys_module; PythonModule m_main_module; PythonModule m_builtins_module; }; TEST_F(PythonDataObjectsTest, TestOwnedReferences) { // After creating a new object, the refcount should be >= 1 PyObject *obj = PyLong_FromLong(3); Py_ssize_t original_refcnt = obj->ob_refcnt; EXPECT_LE(1, original_refcnt); // If we take an owned reference, the refcount should be the same PythonObject owned_long(PyRefType::Owned, obj); EXPECT_EQ(original_refcnt, owned_long.get()->ob_refcnt); // Take another reference and verify that the refcount increases by 1 PythonObject strong_ref(owned_long); EXPECT_EQ(original_refcnt + 1, strong_ref.get()->ob_refcnt); // If we reset the first one, the refcount should be the original value. owned_long.Reset(); EXPECT_EQ(original_refcnt, strong_ref.get()->ob_refcnt); } TEST_F(PythonDataObjectsTest, TestResetting) { PythonDictionary dict(PyInitialValue::Empty); PyObject *new_dict = PyDict_New(); dict.Reset(PyRefType::Owned, new_dict); EXPECT_EQ(new_dict, dict.get()); dict.Reset(PyRefType::Owned, nullptr); EXPECT_EQ(nullptr, dict.get()); dict.Reset(PyRefType::Owned, PyDict_New()); EXPECT_NE(nullptr, dict.get()); dict.Reset(); EXPECT_EQ(nullptr, dict.get()); } TEST_F(PythonDataObjectsTest, TestBorrowedReferences) { PythonInteger long_value(PyRefType::Owned, PyLong_FromLong(3)); Py_ssize_t original_refcnt = long_value.get()->ob_refcnt; EXPECT_LE(1, original_refcnt); PythonInteger borrowed_long(PyRefType::Borrowed, long_value.get()); EXPECT_EQ(original_refcnt + 1, borrowed_long.get()->ob_refcnt); } TEST_F(PythonDataObjectsTest, TestGlobalNameResolutionNoDot) { PythonObject sys_module = m_main_module.ResolveName("sys"); EXPECT_EQ(m_sys_module.get(), sys_module.get()); EXPECT_TRUE(sys_module.IsAllocated()); EXPECT_TRUE(PythonModule::Check(sys_module.get())); } TEST_F(PythonDataObjectsTest, TestModuleNameResolutionNoDot) { PythonObject sys_path = m_sys_module.ResolveName("path"); PythonObject sys_version_info = m_sys_module.ResolveName("version_info"); EXPECT_TRUE(sys_path.IsAllocated()); EXPECT_TRUE(sys_version_info.IsAllocated()); EXPECT_TRUE(PythonList::Check(sys_path.get())); } TEST_F(PythonDataObjectsTest, TestTypeNameResolutionNoDot) { PythonObject sys_version_info = m_sys_module.ResolveName("version_info"); PythonObject version_info_type(PyRefType::Owned, PyObject_Type(sys_version_info.get())); EXPECT_TRUE(version_info_type.IsAllocated()); PythonObject major_version_field = version_info_type.ResolveName("major"); EXPECT_TRUE(major_version_field.IsAllocated()); } TEST_F(PythonDataObjectsTest, TestInstanceNameResolutionNoDot) { PythonObject sys_version_info = m_sys_module.ResolveName("version_info"); PythonObject major_version_field = sys_version_info.ResolveName("major"); PythonObject minor_version_field = sys_version_info.ResolveName("minor"); EXPECT_TRUE(major_version_field.IsAllocated()); EXPECT_TRUE(minor_version_field.IsAllocated()); PythonInteger major_version_value = major_version_field.AsType(); PythonInteger minor_version_value = minor_version_field.AsType(); EXPECT_EQ(PY_MAJOR_VERSION, major_version_value.GetInteger()); EXPECT_EQ(PY_MINOR_VERSION, minor_version_value.GetInteger()); } TEST_F(PythonDataObjectsTest, TestGlobalNameResolutionWithDot) { PythonObject sys_path = m_main_module.ResolveName("sys.path"); EXPECT_TRUE(sys_path.IsAllocated()); EXPECT_TRUE(PythonList::Check(sys_path.get())); PythonInteger version_major = m_main_module.ResolveName("sys.version_info.major") .AsType(); PythonInteger version_minor = m_main_module.ResolveName("sys.version_info.minor") .AsType(); EXPECT_TRUE(version_major.IsAllocated()); EXPECT_TRUE(version_minor.IsAllocated()); EXPECT_EQ(PY_MAJOR_VERSION, version_major.GetInteger()); EXPECT_EQ(PY_MINOR_VERSION, version_minor.GetInteger()); } TEST_F(PythonDataObjectsTest, TestDictionaryResolutionWithDot) { // Make up a custom dictionary with "sys" pointing to the `sys` module. PythonDictionary dict(PyInitialValue::Empty); dict.SetItemForKey(PythonString("sys"), m_sys_module); // Now use that dictionary to resolve `sys.version_info.major` PythonInteger version_major = PythonObject::ResolveNameWithDictionary("sys.version_info.major", dict) .AsType(); PythonInteger version_minor = PythonObject::ResolveNameWithDictionary("sys.version_info.minor", dict) .AsType(); EXPECT_EQ(PY_MAJOR_VERSION, version_major.GetInteger()); EXPECT_EQ(PY_MINOR_VERSION, version_minor.GetInteger()); } TEST_F(PythonDataObjectsTest, TestPythonInteger) { // Test that integers behave correctly when wrapped by a PythonInteger. #if PY_MAJOR_VERSION < 3 // Verify that `PythonInt` works correctly when given a PyInt object. // Note that PyInt doesn't exist in Python 3.x, so this is only for 2.x PyObject *py_int = PyInt_FromLong(12); EXPECT_TRUE(PythonInteger::Check(py_int)); PythonInteger python_int(PyRefType::Owned, py_int); EXPECT_EQ(PyObjectType::Integer, python_int.GetObjectType()); EXPECT_EQ(12, python_int.GetInteger()); #endif // Verify that `PythonInteger` works correctly when given a PyLong object. PyObject *py_long = PyLong_FromLong(12); EXPECT_TRUE(PythonInteger::Check(py_long)); PythonInteger python_long(PyRefType::Owned, py_long); EXPECT_EQ(PyObjectType::Integer, python_long.GetObjectType()); // Verify that you can reset the value and that it is reflected properly. python_long.SetInteger(40); EXPECT_EQ(40, python_long.GetInteger()); // Test that creating a `PythonInteger` object works correctly with the // int constructor. PythonInteger constructed_int(7); EXPECT_EQ(7, constructed_int.GetInteger()); } TEST_F(PythonDataObjectsTest, TestPythonBytes) { static const char *test_bytes = "PythonDataObjectsTest::TestPythonBytes"; PyObject *py_bytes = PyBytes_FromString(test_bytes); EXPECT_TRUE(PythonBytes::Check(py_bytes)); PythonBytes python_bytes(PyRefType::Owned, py_bytes); #if PY_MAJOR_VERSION < 3 EXPECT_TRUE(PythonString::Check(py_bytes)); EXPECT_EQ(PyObjectType::String, python_bytes.GetObjectType()); #else EXPECT_FALSE(PythonString::Check(py_bytes)); EXPECT_EQ(PyObjectType::Bytes, python_bytes.GetObjectType()); #endif llvm::ArrayRef bytes = python_bytes.GetBytes(); EXPECT_EQ(bytes.size(), strlen(test_bytes)); EXPECT_EQ(0, ::memcmp(bytes.data(), test_bytes, bytes.size())); } TEST_F(PythonDataObjectsTest, TestPythonByteArray) { static const char *test_bytes = "PythonDataObjectsTest::TestPythonByteArray"; llvm::StringRef orig_bytes(test_bytes); PyObject *py_bytes = PyByteArray_FromStringAndSize(test_bytes, orig_bytes.size()); EXPECT_TRUE(PythonByteArray::Check(py_bytes)); PythonByteArray python_bytes(PyRefType::Owned, py_bytes); EXPECT_EQ(PyObjectType::ByteArray, python_bytes.GetObjectType()); llvm::ArrayRef after_bytes = python_bytes.GetBytes(); EXPECT_EQ(after_bytes.size(), orig_bytes.size()); EXPECT_EQ(0, ::memcmp(orig_bytes.data(), test_bytes, orig_bytes.size())); } TEST_F(PythonDataObjectsTest, TestPythonString) { // Test that strings behave correctly when wrapped by a PythonString. static const char *test_string = "PythonDataObjectsTest::TestPythonString1"; static const char *test_string2 = "PythonDataObjectsTest::TestPythonString2"; #if PY_MAJOR_VERSION < 3 // Verify that `PythonString` works correctly when given a PyString object. // Note that PyString doesn't exist in Python 3.x, so this is only for 2.x PyObject *py_string = PyString_FromString(test_string); EXPECT_TRUE(PythonString::Check(py_string)); PythonString python_string(PyRefType::Owned, py_string); EXPECT_EQ(PyObjectType::String, python_string.GetObjectType()); EXPECT_STREQ(test_string, python_string.GetString().data()); #else // Verify that `PythonString` works correctly when given a PyUnicode object. PyObject *py_unicode = PyUnicode_FromString(test_string); EXPECT_TRUE(PythonString::Check(py_unicode)); PythonString python_unicode(PyRefType::Owned, py_unicode); EXPECT_EQ(PyObjectType::String, python_unicode.GetObjectType()); EXPECT_STREQ(test_string, python_unicode.GetString().data()); #endif // Test that creating a `PythonString` object works correctly with the // string constructor PythonString constructed_string(test_string2); EXPECT_EQ(test_string2, constructed_string.GetString()); } TEST_F(PythonDataObjectsTest, TestPythonStringToStr) { const char *GetString = "PythonDataObjectsTest::TestPythonStringToStr"; PythonString str(GetString); EXPECT_EQ(GetString, str.GetString()); PythonString str_str = str.Str(); EXPECT_EQ(GetString, str_str.GetString()); } TEST_F(PythonDataObjectsTest, TestPythonIntegerToStr) {} TEST_F(PythonDataObjectsTest, TestPythonIntegerToStructuredInteger) { PythonInteger integer(7); auto int_sp = integer.CreateStructuredInteger(); EXPECT_EQ(7U, int_sp->GetValue()); } TEST_F(PythonDataObjectsTest, TestPythonStringToStructuredString) { static const char *test_string = "PythonDataObjectsTest::TestPythonStringToStructuredString"; PythonString constructed_string(test_string); auto string_sp = constructed_string.CreateStructuredString(); EXPECT_EQ(test_string, string_sp->GetStringValue()); } TEST_F(PythonDataObjectsTest, TestPythonListValueEquality) { // Test that a list which is built through the native // Python API behaves correctly when wrapped by a PythonList. static const unsigned list_size = 2; static const long long_value0 = 5; static const char *const string_value1 = "String Index 1"; PyObject *py_list = PyList_New(2); EXPECT_TRUE(PythonList::Check(py_list)); PythonList list(PyRefType::Owned, py_list); PythonObject list_items[list_size]; list_items[0].Reset(PythonInteger(long_value0)); list_items[1].Reset(PythonString(string_value1)); for (unsigned i = 0; i < list_size; ++i) list.SetItemAtIndex(i, list_items[i]); EXPECT_EQ(list_size, list.GetSize()); EXPECT_EQ(PyObjectType::List, list.GetObjectType()); // Verify that the values match PythonObject chk_value1 = list.GetItemAtIndex(0); PythonObject chk_value2 = list.GetItemAtIndex(1); EXPECT_TRUE(PythonInteger::Check(chk_value1.get())); EXPECT_TRUE(PythonString::Check(chk_value2.get())); PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get()); PythonString chk_str(PyRefType::Borrowed, chk_value2.get()); EXPECT_EQ(long_value0, chk_int.GetInteger()); EXPECT_EQ(string_value1, chk_str.GetString()); } TEST_F(PythonDataObjectsTest, TestPythonListManipulation) { // Test that manipulation of a PythonList behaves correctly when // wrapped by a PythonDictionary. static const long long_value0 = 5; static const char *const string_value1 = "String Index 1"; PythonList list(PyInitialValue::Empty); PythonInteger integer(long_value0); PythonString string(string_value1); list.AppendItem(integer); list.AppendItem(string); EXPECT_EQ(2U, list.GetSize()); // Verify that the values match PythonObject chk_value1 = list.GetItemAtIndex(0); PythonObject chk_value2 = list.GetItemAtIndex(1); EXPECT_TRUE(PythonInteger::Check(chk_value1.get())); EXPECT_TRUE(PythonString::Check(chk_value2.get())); PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get()); PythonString chk_str(PyRefType::Borrowed, chk_value2.get()); EXPECT_EQ(long_value0, chk_int.GetInteger()); EXPECT_EQ(string_value1, chk_str.GetString()); } TEST_F(PythonDataObjectsTest, TestPythonListToStructuredList) { static const long long_value0 = 5; static const char *const string_value1 = "String Index 1"; PythonList list(PyInitialValue::Empty); list.AppendItem(PythonInteger(long_value0)); list.AppendItem(PythonString(string_value1)); auto array_sp = list.CreateStructuredArray(); - EXPECT_EQ(StructuredData::Type::eTypeInteger, + EXPECT_EQ(lldb::eStructuredDataTypeInteger, array_sp->GetItemAtIndex(0)->GetType()); - EXPECT_EQ(StructuredData::Type::eTypeString, + EXPECT_EQ(lldb::eStructuredDataTypeString, array_sp->GetItemAtIndex(1)->GetType()); auto int_sp = array_sp->GetItemAtIndex(0)->GetAsInteger(); auto string_sp = array_sp->GetItemAtIndex(1)->GetAsString(); EXPECT_EQ(long_value0, long(int_sp->GetValue())); EXPECT_EQ(string_value1, string_sp->GetValue()); } TEST_F(PythonDataObjectsTest, TestPythonTupleSize) { PythonTuple tuple(PyInitialValue::Empty); EXPECT_EQ(0U, tuple.GetSize()); tuple = PythonTuple(3); EXPECT_EQ(3U, tuple.GetSize()); } TEST_F(PythonDataObjectsTest, TestPythonTupleValues) { PythonTuple tuple(3); PythonInteger int_value(1); PythonString string_value("Test"); PythonObject none_value(PyRefType::Borrowed, Py_None); tuple.SetItemAtIndex(0, int_value); tuple.SetItemAtIndex(1, string_value); tuple.SetItemAtIndex(2, none_value); EXPECT_EQ(tuple.GetItemAtIndex(0).get(), int_value.get()); EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get()); EXPECT_EQ(tuple.GetItemAtIndex(2).get(), none_value.get()); } TEST_F(PythonDataObjectsTest, TestPythonTupleInitializerList) { PythonInteger int_value(1); PythonString string_value("Test"); PythonObject none_value(PyRefType::Borrowed, Py_None); PythonTuple tuple{int_value, string_value, none_value}; EXPECT_EQ(3U, tuple.GetSize()); EXPECT_EQ(tuple.GetItemAtIndex(0).get(), int_value.get()); EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get()); EXPECT_EQ(tuple.GetItemAtIndex(2).get(), none_value.get()); } TEST_F(PythonDataObjectsTest, TestPythonTupleInitializerList2) { PythonInteger int_value(1); PythonString string_value("Test"); PythonObject none_value(PyRefType::Borrowed, Py_None); PythonTuple tuple{int_value.get(), string_value.get(), none_value.get()}; EXPECT_EQ(3U, tuple.GetSize()); EXPECT_EQ(tuple.GetItemAtIndex(0).get(), int_value.get()); EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get()); EXPECT_EQ(tuple.GetItemAtIndex(2).get(), none_value.get()); } TEST_F(PythonDataObjectsTest, TestPythonTupleToStructuredList) { PythonInteger int_value(1); PythonString string_value("Test"); PythonTuple tuple{int_value.get(), string_value.get()}; auto array_sp = tuple.CreateStructuredArray(); EXPECT_EQ(tuple.GetSize(), array_sp->GetSize()); - EXPECT_EQ(StructuredData::Type::eTypeInteger, + EXPECT_EQ(lldb::eStructuredDataTypeInteger, array_sp->GetItemAtIndex(0)->GetType()); - EXPECT_EQ(StructuredData::Type::eTypeString, + EXPECT_EQ(lldb::eStructuredDataTypeString, array_sp->GetItemAtIndex(1)->GetType()); } TEST_F(PythonDataObjectsTest, TestPythonDictionaryValueEquality) { // Test that a dictionary which is built through the native // Python API behaves correctly when wrapped by a PythonDictionary. static const unsigned dict_entries = 2; const char *key_0 = "Key 0"; int key_1 = 1; const int value_0 = 0; const char *value_1 = "Value 1"; PythonObject py_keys[dict_entries]; PythonObject py_values[dict_entries]; py_keys[0].Reset(PythonString(key_0)); py_keys[1].Reset(PythonInteger(key_1)); py_values[0].Reset(PythonInteger(value_0)); py_values[1].Reset(PythonString(value_1)); PyObject *py_dict = PyDict_New(); EXPECT_TRUE(PythonDictionary::Check(py_dict)); PythonDictionary dict(PyRefType::Owned, py_dict); for (unsigned i = 0; i < dict_entries; ++i) PyDict_SetItem(py_dict, py_keys[i].get(), py_values[i].get()); EXPECT_EQ(dict.GetSize(), dict_entries); EXPECT_EQ(PyObjectType::Dictionary, dict.GetObjectType()); // Verify that the values match PythonObject chk_value1 = dict.GetItemForKey(py_keys[0]); PythonObject chk_value2 = dict.GetItemForKey(py_keys[1]); EXPECT_TRUE(PythonInteger::Check(chk_value1.get())); EXPECT_TRUE(PythonString::Check(chk_value2.get())); PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get()); PythonString chk_str(PyRefType::Borrowed, chk_value2.get()); EXPECT_EQ(value_0, chk_int.GetInteger()); EXPECT_EQ(value_1, chk_str.GetString()); } TEST_F(PythonDataObjectsTest, TestPythonDictionaryManipulation) { // Test that manipulation of a dictionary behaves correctly when wrapped // by a PythonDictionary. static const unsigned dict_entries = 2; const char *const key_0 = "Key 0"; const char *const key_1 = "Key 1"; const long value_0 = 1; const char *const value_1 = "Value 1"; PythonString keys[dict_entries]; PythonObject values[dict_entries]; keys[0].Reset(PythonString(key_0)); keys[1].Reset(PythonString(key_1)); values[0].Reset(PythonInteger(value_0)); values[1].Reset(PythonString(value_1)); PythonDictionary dict(PyInitialValue::Empty); for (int i = 0; i < 2; ++i) dict.SetItemForKey(keys[i], values[i]); EXPECT_EQ(dict_entries, dict.GetSize()); // Verify that the keys and values match PythonObject chk_value1 = dict.GetItemForKey(keys[0]); PythonObject chk_value2 = dict.GetItemForKey(keys[1]); EXPECT_TRUE(PythonInteger::Check(chk_value1.get())); EXPECT_TRUE(PythonString::Check(chk_value2.get())); PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get()); PythonString chk_str(PyRefType::Borrowed, chk_value2.get()); EXPECT_EQ(value_0, chk_int.GetInteger()); EXPECT_EQ(value_1, chk_str.GetString()); } TEST_F(PythonDataObjectsTest, TestPythonDictionaryToStructuredDictionary) { static const char *const string_key0 = "String Key 0"; static const char *const string_key1 = "String Key 1"; static const char *const string_value0 = "String Value 0"; static const long int_value1 = 7; PythonDictionary dict(PyInitialValue::Empty); dict.SetItemForKey(PythonString(string_key0), PythonString(string_value0)); dict.SetItemForKey(PythonString(string_key1), PythonInteger(int_value1)); auto dict_sp = dict.CreateStructuredDictionary(); EXPECT_EQ(2U, dict_sp->GetSize()); EXPECT_TRUE(dict_sp->HasKey(string_key0)); EXPECT_TRUE(dict_sp->HasKey(string_key1)); auto string_sp = dict_sp->GetValueForKey(string_key0)->GetAsString(); auto int_sp = dict_sp->GetValueForKey(string_key1)->GetAsInteger(); EXPECT_EQ(string_value0, string_sp->GetValue()); EXPECT_EQ(int_value1, long(int_sp->GetValue())); } TEST_F(PythonDataObjectsTest, TestPythonCallableCheck) { PythonObject sys_exc_info = m_sys_module.ResolveName("exc_info"); PythonObject none(PyRefType::Borrowed, Py_None); EXPECT_TRUE(PythonCallable::Check(sys_exc_info.get())); EXPECT_FALSE(PythonCallable::Check(none.get())); } TEST_F(PythonDataObjectsTest, TestPythonCallableInvoke) { auto list = m_builtins_module.ResolveName("list").AsType(); PythonInteger one(1); PythonString two("two"); PythonTuple three = {one, two}; PythonTuple tuple_to_convert = {one, two, three}; PythonObject result = list({tuple_to_convert}); EXPECT_TRUE(PythonList::Check(result.get())); auto list_result = result.AsType(); EXPECT_EQ(3U, list_result.GetSize()); EXPECT_EQ(one.get(), list_result.GetItemAtIndex(0).get()); EXPECT_EQ(two.get(), list_result.GetItemAtIndex(1).get()); EXPECT_EQ(three.get(), list_result.GetItemAtIndex(2).get()); } TEST_F(PythonDataObjectsTest, TestPythonFile) { File file(FileSystem::DEV_NULL, File::eOpenOptionRead); PythonFile py_file(file, "r"); EXPECT_TRUE(PythonFile::Check(py_file.get())); } TEST_F(PythonDataObjectsTest, TestObjectAttributes) { PythonInteger py_int(42); EXPECT_TRUE(py_int.HasAttribute("numerator")); EXPECT_FALSE(py_int.HasAttribute("this_should_not_exist")); PythonInteger numerator_attr = py_int.GetAttributeValue("numerator").AsType(); EXPECT_TRUE(numerator_attr.IsAllocated()); EXPECT_EQ(42, numerator_attr.GetInteger()); } TEST_F(PythonDataObjectsTest, TestExtractingUInt64ThroughStructuredData) { // Make up a custom dictionary with "sys" pointing to the `sys` module. const char *key_name = "addr"; const uint64_t value = 0xf000000000000000ull; PythonDictionary python_dict(PyInitialValue::Empty); PythonInteger python_ull_value(PyRefType::Owned, PyLong_FromUnsignedLongLong(value)); python_dict.SetItemForKey(PythonString(key_name), python_ull_value); StructuredData::ObjectSP structured_data_sp = python_dict.CreateStructuredObject(); EXPECT_TRUE((bool)structured_data_sp); if (structured_data_sp) { StructuredData::Dictionary *structured_dict_ptr = structured_data_sp->GetAsDictionary(); EXPECT_TRUE(structured_dict_ptr != nullptr); if (structured_dict_ptr) { StructuredData::ObjectSP structured_addr_value_sp = structured_dict_ptr->GetValueForKey(key_name); EXPECT_TRUE((bool)structured_addr_value_sp); const uint64_t extracted_value = structured_addr_value_sp->GetIntegerValue(123); EXPECT_TRUE(extracted_value == value); } } }