1 //===-- TraceIntelPT.cpp --------------------------------------------------===//
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 #include "TraceIntelPT.h"
10 
11 #include "CommandObjectTraceStartIntelPT.h"
12 #include "TraceIntelPTSessionFileParser.h"
13 #include "lldb/Core/PluginManager.h"
14 #include "lldb/Target/Process.h"
15 #include "lldb/Target/Target.h"
16 #include "lldb/Target/ThreadPostMortemTrace.h"
17 
18 using namespace lldb;
19 using namespace lldb_private;
20 using namespace lldb_private::trace_intel_pt;
21 using namespace llvm;
22 
23 LLDB_PLUGIN_DEFINE(TraceIntelPT)
24 
25 lldb::CommandObjectSP
26 TraceIntelPT::GetProcessTraceStartCommand(CommandInterpreter &interpreter) {
27   return CommandObjectSP(
28       new CommandObjectProcessTraceStartIntelPT(*this, interpreter));
29 }
30 
31 lldb::CommandObjectSP
32 TraceIntelPT::GetThreadTraceStartCommand(CommandInterpreter &interpreter) {
33   return CommandObjectSP(
34       new CommandObjectThreadTraceStartIntelPT(*this, interpreter));
35 }
36 
37 void TraceIntelPT::Initialize() {
38   PluginManager::RegisterPlugin(GetPluginNameStatic(), "Intel Processor Trace",
39                                 CreateInstanceForSessionFile,
40                                 CreateInstanceForLiveProcess,
41                                 TraceIntelPTSessionFileParser::GetSchema());
42 }
43 
44 void TraceIntelPT::Terminate() {
45   PluginManager::UnregisterPlugin(CreateInstanceForSessionFile);
46 }
47 
48 ConstString TraceIntelPT::GetPluginNameStatic() {
49   static ConstString g_name("intel-pt");
50   return g_name;
51 }
52 
53 StringRef TraceIntelPT::GetSchema() {
54   return TraceIntelPTSessionFileParser::GetSchema();
55 }
56 
57 //------------------------------------------------------------------
58 // PluginInterface protocol
59 //------------------------------------------------------------------
60 
61 ConstString TraceIntelPT::GetPluginName() { return GetPluginNameStatic(); }
62 
63 uint32_t TraceIntelPT::GetPluginVersion() { return 1; }
64 
65 void TraceIntelPT::Dump(Stream *s) const {}
66 
67 Expected<TraceSP> TraceIntelPT::CreateInstanceForSessionFile(
68     const json::Value &trace_session_file, StringRef session_file_dir,
69     Debugger &debugger) {
70   return TraceIntelPTSessionFileParser(debugger, trace_session_file,
71                                        session_file_dir)
72       .Parse();
73 }
74 
75 Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) {
76   TraceSP instance(new TraceIntelPT(process));
77   process.GetTarget().SetTrace(instance);
78   return instance;
79 }
80 
81 TraceIntelPT::TraceIntelPT(
82     const pt_cpu &cpu_info,
83     const std::vector<ThreadPostMortemTraceSP> &traced_threads)
84     : m_cpu_info(cpu_info) {
85   for (const ThreadPostMortemTraceSP &thread : traced_threads)
86     m_thread_decoders.emplace(
87         thread.get(), std::make_unique<PostMortemThreadDecoder>(thread, *this));
88 }
89 
90 const DecodedThread *TraceIntelPT::Decode(Thread &thread) {
91   RefreshLiveProcessState();
92   if (m_failed_live_threads_decoder.hasValue())
93     return &*m_failed_live_threads_decoder;
94 
95   auto it = m_thread_decoders.find(&thread);
96   if (it == m_thread_decoders.end())
97     return nullptr;
98   return &it->second->Decode();
99 }
100 
101 size_t TraceIntelPT::GetCursorPosition(Thread &thread) {
102   const DecodedThread *decoded_thread = Decode(thread);
103   if (!decoded_thread)
104     return 0;
105   return decoded_thread->GetCursorPosition();
106 }
107 
108 void TraceIntelPT::TraverseInstructions(
109     Thread &thread, size_t position, TraceDirection direction,
110     std::function<bool(size_t index, Expected<lldb::addr_t> load_addr)>
111         callback) {
112   const DecodedThread *decoded_thread = Decode(thread);
113   if (!decoded_thread)
114     return;
115 
116   ArrayRef<IntelPTInstruction> instructions = decoded_thread->GetInstructions();
117 
118   ssize_t delta = direction == TraceDirection::Forwards ? 1 : -1;
119   for (ssize_t i = position; i < (ssize_t)instructions.size() && i >= 0;
120        i += delta)
121     if (!callback(i, instructions[i].GetLoadAddress()))
122       break;
123 }
124 
125 Optional<size_t> TraceIntelPT::GetInstructionCount(Thread &thread) {
126   if (const DecodedThread *decoded_thread = Decode(thread))
127     return decoded_thread->GetInstructions().size();
128   else
129     return None;
130 }
131 
132 Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() {
133   Expected<std::vector<uint8_t>> cpu_info = GetLiveProcessBinaryData("cpuInfo");
134   if (!cpu_info)
135     return cpu_info.takeError();
136 
137   int64_t cpu_family = -1;
138   int64_t model = -1;
139   int64_t stepping = -1;
140   std::string vendor_id;
141 
142   StringRef rest(reinterpret_cast<const char *>(cpu_info->data()),
143                  cpu_info->size());
144   while (!rest.empty()) {
145     StringRef line;
146     std::tie(line, rest) = rest.split('\n');
147 
148     SmallVector<StringRef, 2> columns;
149     line.split(columns, StringRef(":"), -1, false);
150 
151     if (columns.size() < 2)
152       continue; // continue searching
153 
154     columns[1] = columns[1].trim(" ");
155     if (columns[0].contains("cpu family") &&
156         columns[1].getAsInteger(10, cpu_family))
157       continue;
158 
159     else if (columns[0].contains("model") && columns[1].getAsInteger(10, model))
160       continue;
161 
162     else if (columns[0].contains("stepping") &&
163              columns[1].getAsInteger(10, stepping))
164       continue;
165 
166     else if (columns[0].contains("vendor_id")) {
167       vendor_id = columns[1].str();
168       if (!vendor_id.empty())
169         continue;
170     }
171 
172     if ((cpu_family != -1) && (model != -1) && (stepping != -1) &&
173         (!vendor_id.empty())) {
174       return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown,
175                     static_cast<uint16_t>(cpu_family),
176                     static_cast<uint8_t>(model),
177                     static_cast<uint8_t>(stepping)};
178     }
179   }
180   return createStringError(inconvertibleErrorCode(),
181                            "Failed parsing the target's /proc/cpuinfo file");
182 }
183 
184 Expected<pt_cpu> TraceIntelPT::GetCPUInfo() {
185   if (!m_cpu_info) {
186     if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess())
187       m_cpu_info = *cpu_info;
188     else
189       return cpu_info.takeError();
190   }
191   return *m_cpu_info;
192 }
193 
194 void TraceIntelPT::DoRefreshLiveProcessState(
195     Expected<TraceGetStateResponse> state) {
196   m_thread_decoders.clear();
197 
198   if (!state) {
199     m_failed_live_threads_decoder = DecodedThread(state.takeError());
200     return;
201   }
202 
203   for (const TraceThreadState &thread_state : state->tracedThreads) {
204     Thread &thread =
205         *m_live_process->GetThreadList().FindThreadByID(thread_state.tid);
206     m_thread_decoders.emplace(
207         &thread, std::make_unique<LiveThreadDecoder>(thread, *this));
208   }
209 }
210 
211 bool TraceIntelPT::IsTraced(const Thread &thread) {
212   return m_thread_decoders.count(&thread);
213 }
214 
215 Error TraceIntelPT::Start(size_t thread_buffer_size,
216                           size_t total_buffer_size_limit) {
217   TraceIntelPTStartRequest request;
218   request.threadBufferSize = thread_buffer_size;
219   request.processBufferSizeLimit = total_buffer_size_limit;
220   request.type = GetPluginName().AsCString();
221   return Trace::Start(toJSON(request));
222 }
223 
224 llvm::Error TraceIntelPT::Start(const std::vector<lldb::tid_t> &tids,
225                                 size_t thread_buffer_size) {
226   TraceIntelPTStartRequest request;
227   request.threadBufferSize = thread_buffer_size;
228   request.type = GetPluginName().AsCString();
229   request.tids.emplace();
230   for (lldb::tid_t tid : tids)
231     request.tids->push_back(tid);
232   return Trace::Start(toJSON(request));
233 }
234 
235 Expected<std::vector<uint8_t>>
236 TraceIntelPT::GetLiveThreadBuffer(lldb::tid_t tid) {
237   return Trace::GetLiveThreadBinaryData(tid, "threadTraceBuffer");
238 }
239