1 //===-- Decoder.h -----------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef Decoder_h_
10 #define Decoder_h_
11 
12 // C/C++ Includes
13 #include <map>
14 #include <mutex>
15 #include <string>
16 #include <vector>
17 
18 #include "lldb/API/SBDebugger.h"
19 #include "lldb/API/SBError.h"
20 #include "lldb/API/SBProcess.h"
21 #include "lldb/API/SBStream.h"
22 #include "lldb/API/SBStructuredData.h"
23 #include "lldb/API/SBTarget.h"
24 #include "lldb/API/SBTrace.h"
25 #include "lldb/API/SBTraceOptions.h"
26 #include "lldb/lldb-enumerations.h"
27 #include "lldb/lldb-types.h"
28 
29 #include "intel-pt.h"
30 
31 namespace ptdecoder_private {
32 /// \class Instruction
33 /// Represents an assembly instruction containing raw
34 ///     instruction bytes, instruction address along with information
35 ///     regarding execution flow context and Intel(R) Processor Trace
36 ///     context.
37 class Instruction {
38 public:
Instruction()39   Instruction() : ip(0), data(), error(), iclass(ptic_error), speculative(0) {}
40 
41   Instruction(const Instruction &insn) = default;
42 
Instruction(const struct pt_insn & insn)43   Instruction(const struct pt_insn &insn)
44       : ip(insn.ip), data(), error(insn.size == 0 ? "invalid instruction" : ""),
45         iclass(insn.iclass), speculative(insn.speculative) {
46     if (insn.size != 0)
47       data.assign(insn.raw, insn.raw + insn.size);
48   }
49 
Instruction(const char * err)50   Instruction(const char *err)
51       : ip(0), data(), error(err ? err : "unknown error"), iclass(ptic_error),
52         speculative(0) {}
53 
~Instruction()54   ~Instruction() {}
55 
GetInsnAddress()56   uint64_t GetInsnAddress() const { return ip; }
57 
GetRawBytes(void * buf,size_t size)58   size_t GetRawBytes(void *buf, size_t size) const {
59     if ((buf == nullptr) || (size == 0))
60       return data.size();
61 
62     size_t bytes_to_read = ((size <= data.size()) ? size : data.size());
63     ::memcpy(buf, data.data(), bytes_to_read);
64     return bytes_to_read;
65   }
66 
GetError()67   const std::string &GetError() const { return error; }
68 
GetSpeculative()69   bool GetSpeculative() const { return speculative; }
70 
71 private:
72   uint64_t ip;               // instruction address in inferior's memory image
73   std::vector<uint8_t> data; // raw bytes
74   std::string error;         // Error string if instruction is invalid
75   enum pt_insn_class iclass; // classification of the instruction
76   // A collection of flags giving additional information about instruction
77   uint32_t speculative : 1; // Instruction was executed speculatively or not
78 };
79 
80 /// \class InstructionList
81 /// Represents a list of assembly instructions. Each instruction is of
82 ///     type Instruction.
83 class InstructionList {
84 public:
InstructionList()85   InstructionList() : m_insn_vec() {}
86 
InstructionList(const InstructionList & insn_list)87   InstructionList(const InstructionList &insn_list)
88       : m_insn_vec(insn_list.m_insn_vec) {}
89 
~InstructionList()90   ~InstructionList() {}
91 
92   // Get number of instructions in the list
GetSize()93   size_t GetSize() const { return m_insn_vec.size(); }
94 
95   // Get instruction at index
GetInstructionAtIndex(uint32_t idx)96   Instruction GetInstructionAtIndex(uint32_t idx) {
97     return (idx < m_insn_vec.size() ? m_insn_vec[idx]
98                                     : Instruction("invalid instruction"));
99   }
100 
101   // Append intruction at the end of the list
AppendInstruction(Instruction inst)102   void AppendInstruction(Instruction inst) { m_insn_vec.push_back(inst); }
103 
104 private:
105   std::vector<Instruction> m_insn_vec;
106 };
107 
108 /// \class TraceOptions
109 /// Provides Intel(R) Processor Trace specific configuration options and
110 ///     other information obtained by decoding and post-processing the trace
111 ///     data. Currently, this information comprises of the total number of
112 ///     assembly instructions executed for an inferior.
113 class TraceOptions : public lldb::SBTraceOptions {
114 public:
TraceOptions()115   TraceOptions() : lldb::SBTraceOptions(), m_insn_log_size(0) {}
116 
~TraceOptions()117   ~TraceOptions() {}
118 
119   /// Get total number of assembly instructions obtained after decoding the
120   /// complete Intel(R) Processor Trace data obtained from LLDB.
121   ///
122   /// \return
123   ///     Total number of instructions.
getInstructionLogSize()124   uint32_t getInstructionLogSize() const { return m_insn_log_size; }
125 
126   /// Set total number of assembly instructions.
127   ///
128   /// \param[in] size
129   ///     Value to be set.
setInstructionLogSize(uint32_t size)130   void setInstructionLogSize(uint32_t size) { m_insn_log_size = size; }
131 
132 private:
133   uint32_t m_insn_log_size;
134 };
135 
136 /// \class Decoder
137 /// This class makes use of Intel(R) Processor Trace hardware feature
138 ///     (implememted inside LLDB) to gather trace data for an inferior (being
139 ///     debugged with LLDB) to provide meaningful information out of it.
140 ///
141 ///     Currently the meaningful information comprises of the execution flow
142 ///     of the inferior (in terms of assembly instructions executed). The class
143 ///     enables user to:
144 ///     - start the trace with configuration options for a thread/process,
145 ///     - stop the trace for a thread/process,
146 ///     - get the execution flow (assembly instructions) for a thread and
147 ///     - get trace specific information for a thread
148 class Decoder {
149 public:
150   typedef std::vector<Instruction> Instructions;
151 
Decoder(lldb::SBDebugger & sbdebugger)152   Decoder(lldb::SBDebugger &sbdebugger)
153       : m_mapProcessUID_mapThreadID_TraceInfo_mutex(),
154         m_mapProcessUID_mapThreadID_TraceInfo(),
155         m_debugger_user_id(sbdebugger.GetID()) {}
156 
~Decoder()157   ~Decoder() {}
158 
159   void StartProcessorTrace(lldb::SBProcess &sbprocess,
160                            lldb::SBTraceOptions &sbtraceoptions,
161                            lldb::SBError &sberror);
162 
163   void StopProcessorTrace(lldb::SBProcess &sbprocess, lldb::SBError &sberror,
164                           lldb::tid_t tid = LLDB_INVALID_THREAD_ID);
165 
166   void GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, lldb::tid_t tid,
167                                  uint32_t offset, uint32_t count,
168                                  InstructionList &result_list,
169                                  lldb::SBError &sberror);
170 
171   void GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid,
172                              TraceOptions &traceinfo, lldb::SBError &sberror);
173 
174 private:
175   class ThreadTraceInfo;
176   typedef std::vector<uint8_t> Buffer;
177 
178   // internal class to manage inferior's read-execute section information
179   class ReadExecuteSectionInfo {
180   public:
181     uint64_t load_address;
182     uint64_t file_offset;
183     uint64_t size;
184     std::string image_path;
185 
ReadExecuteSectionInfo(const uint64_t addr,const uint64_t offset,const uint64_t sz,const std::string & path)186     ReadExecuteSectionInfo(const uint64_t addr, const uint64_t offset,
187                            const uint64_t sz, const std::string &path)
188         : load_address(addr), file_offset(offset), size(sz), image_path(path) {}
189 
190     ReadExecuteSectionInfo(const ReadExecuteSectionInfo &rxsection) = default;
191   };
192 
193   typedef struct pt_cpu CPUInfo;
194   typedef std::vector<ReadExecuteSectionInfo> ReadExecuteSectionInfos;
195 
196   // Check whether the provided SBProcess belongs to the same SBDebugger with
197   // which Decoder class instance was constructed.
198   void CheckDebuggerID(lldb::SBProcess &sbprocess, lldb::SBError &sberror);
199 
200   // Function to remove entries of finished processes/threads in the class
201   void RemoveDeadProcessesAndThreads(lldb::SBProcess &sbprocess);
202 
203   // Parse cpu information from trace configuration received from LLDB
204   void ParseCPUInfo(CPUInfo &pt_cpu, lldb::SBStructuredData &s,
205                     lldb::SBError &sberror);
206 
207   /// Function performs following tasks for a given process and thread:
208   ///  - Checks if the given thread is registered in the class or not. If not
209   ///  then tries to register it if trace was ever started on the entire
210   ///  process. Else returns error.
211   ///  - fetches trace and other necessary information from LLDB (using
212   ///  ReadTraceDataAndImageInfo()) and decodes the trace (using
213   ///  DecodeProcessorTrace())
214   void FetchAndDecode(lldb::SBProcess &sbprocess, lldb::tid_t tid,
215                       lldb::SBError &sberror,
216                       ThreadTraceInfo **threadTraceInfo);
217 
218   // Helper function of FetchAndDecode() to get raw trace data and memory image
219   // info of inferior from LLDB
220   void ReadTraceDataAndImageInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid,
221                                  lldb::SBError &sberror,
222                                  ThreadTraceInfo &threadTraceInfo);
223 
224   // Helper function of FetchAndDecode() to initialize raw trace decoder and
225   // start trace decoding
226   void DecodeProcessorTrace(lldb::SBProcess &sbprocess, lldb::tid_t tid,
227                             lldb::SBError &sberror,
228                             ThreadTraceInfo &threadTraceInfo);
229 
230   // Helper function of ReadTraceDataAndImageInfo() function for gathering
231   // inferior's memory image info along with all dynamic libraries linked with
232   // it
233   void GetTargetModulesInfo(lldb::SBTarget &sbtarget,
234                             ReadExecuteSectionInfos &readExecuteSectionInfos,
235                             lldb::SBError &sberror);
236 
237   /// Helper functions of DecodeProcessorTrace() function for:
238   ///  - initializing raw trace decoder (provided by Intel(R) Processor Trace
239   ///    Decoding library)
240   ///  - start trace decoding
241   void InitializePTInstDecoder(
242       struct pt_insn_decoder **decoder, struct pt_config *config,
243       const CPUInfo &pt_cpu, Buffer &pt_buffer,
244       const ReadExecuteSectionInfos &readExecuteSectionInfos,
245       lldb::SBError &sberror) const;
246   void DecodeTrace(struct pt_insn_decoder *decoder,
247                    Instructions &instruction_list, lldb::SBError &sberror);
248   int HandlePTInstructionEvents(pt_insn_decoder *decoder, int errcode,
249                                 Instructions &instruction_list,
250                                 lldb::SBError &sberror);
251 
252   int AppendErrorToInstructionList(int errcode, pt_insn_decoder *decoder,
253                                    Instructions &instruction_list,
254                                    lldb::SBError &sberror);
255 
256   void AppendErrorWithOffsetToInstructionList(int errcode,
257                                               uint64_t decoder_offset,
258                                               Instructions &instruction_list,
259                                               lldb::SBError &sberror);
260 
261   void AppendErrorWithoutOffsetToInstructionList(int errcode,
262                                                  Instructions &instruction_list,
263                                                  lldb::SBError &sberror);
264 
265   // Function to diagnose and indicate errors during raw trace decoding
266   void Diagnose(struct pt_insn_decoder *decoder, int errcode,
267                 lldb::SBError &sberror, const struct pt_insn *insn = nullptr);
268 
269   class ThreadTraceInfo {
270   public:
ThreadTraceInfo()271     ThreadTraceInfo()
272         : m_pt_buffer(), m_readExecuteSectionInfos(), m_thread_stop_id(0),
273           m_trace(), m_pt_cpu(), m_instruction_log() {}
274 
275     ThreadTraceInfo(const ThreadTraceInfo &trace_info) = default;
276 
~ThreadTraceInfo()277     ~ThreadTraceInfo() {}
278 
GetPTBuffer()279     Buffer &GetPTBuffer() { return m_pt_buffer; }
280 
AllocatePTBuffer(uint64_t size)281     void AllocatePTBuffer(uint64_t size) { m_pt_buffer.assign(size, 0); }
282 
GetReadExecuteSectionInfos()283     ReadExecuteSectionInfos &GetReadExecuteSectionInfos() {
284       return m_readExecuteSectionInfos;
285     }
286 
GetCPUInfo()287     CPUInfo &GetCPUInfo() { return m_pt_cpu; }
288 
GetInstructionLog()289     Instructions &GetInstructionLog() { return m_instruction_log; }
290 
GetStopID()291     uint32_t GetStopID() const { return m_thread_stop_id; }
292 
SetStopID(uint32_t stop_id)293     void SetStopID(uint32_t stop_id) { m_thread_stop_id = stop_id; }
294 
GetUniqueTraceInstance()295     lldb::SBTrace &GetUniqueTraceInstance() { return m_trace; }
296 
SetUniqueTraceInstance(lldb::SBTrace & trace)297     void SetUniqueTraceInstance(lldb::SBTrace &trace) { m_trace = trace; }
298 
299     friend class Decoder;
300 
301   private:
302     Buffer m_pt_buffer; // raw trace buffer
303     ReadExecuteSectionInfos
304         m_readExecuteSectionInfos; // inferior's memory image info
305     uint32_t m_thread_stop_id;     // stop id for thread
306     lldb::SBTrace m_trace; // unique tracing instance of a thread/process
307     CPUInfo m_pt_cpu; // cpu info of the target on which inferior is running
308     Instructions m_instruction_log; // complete instruction log
309   };
310 
311   typedef std::map<lldb::user_id_t, ThreadTraceInfo> MapThreadID_TraceInfo;
312   typedef std::map<uint32_t, MapThreadID_TraceInfo>
313       MapProcessUID_MapThreadID_TraceInfo;
314 
315   std::mutex m_mapProcessUID_mapThreadID_TraceInfo_mutex;
316   MapProcessUID_MapThreadID_TraceInfo
317       m_mapProcessUID_mapThreadID_TraceInfo; // to store trace information for
318                                              // each process and its associated
319                                              // threads
320   lldb::user_id_t m_debugger_user_id; // SBDebugger instance which is associated
321                                       // to this Decoder instance
322 };
323 
324 } // namespace ptdecoder_private
325 #endif // Decoder_h_
326