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 "../common/ThreadPostMortemTrace.h" 12 #include "CommandObjectTraceStartIntelPT.h" 13 #include "DecodedThread.h" 14 #include "TraceIntelPTConstants.h" 15 #include "TraceIntelPTSessionFileParser.h" 16 #include "TraceIntelPTSessionSaver.h" 17 #include "lldb/Core/PluginManager.h" 18 #include "lldb/Target/Process.h" 19 #include "lldb/Target/Target.h" 20 #include "llvm/ADT/None.h" 21 22 using namespace lldb; 23 using namespace lldb_private; 24 using namespace lldb_private::trace_intel_pt; 25 using namespace llvm; 26 27 LLDB_PLUGIN_DEFINE(TraceIntelPT) 28 29 lldb::CommandObjectSP 30 TraceIntelPT::GetProcessTraceStartCommand(CommandInterpreter &interpreter) { 31 return CommandObjectSP( 32 new CommandObjectProcessTraceStartIntelPT(*this, interpreter)); 33 } 34 35 lldb::CommandObjectSP 36 TraceIntelPT::GetThreadTraceStartCommand(CommandInterpreter &interpreter) { 37 return CommandObjectSP( 38 new CommandObjectThreadTraceStartIntelPT(*this, interpreter)); 39 } 40 41 void TraceIntelPT::Initialize() { 42 PluginManager::RegisterPlugin(GetPluginNameStatic(), "Intel Processor Trace", 43 CreateInstanceForSessionFile, 44 CreateInstanceForLiveProcess, 45 TraceIntelPTSessionFileParser::GetSchema()); 46 } 47 48 void TraceIntelPT::Terminate() { 49 PluginManager::UnregisterPlugin(CreateInstanceForSessionFile); 50 } 51 52 ConstString TraceIntelPT::GetPluginNameStatic() { 53 static ConstString g_name("intel-pt"); 54 return g_name; 55 } 56 57 StringRef TraceIntelPT::GetSchema() { 58 return TraceIntelPTSessionFileParser::GetSchema(); 59 } 60 61 //------------------------------------------------------------------ 62 // PluginInterface protocol 63 //------------------------------------------------------------------ 64 65 ConstString TraceIntelPT::GetPluginName() { return GetPluginNameStatic(); } 66 67 void TraceIntelPT::Dump(Stream *s) const {} 68 69 llvm::Error TraceIntelPT::SaveLiveTraceToDisk(FileSpec directory) { 70 RefreshLiveProcessState(); 71 return TraceIntelPTSessionSaver().SaveToDisk(*this, directory); 72 } 73 74 Expected<TraceSP> TraceIntelPT::CreateInstanceForSessionFile( 75 const json::Value &trace_session_file, StringRef session_file_dir, 76 Debugger &debugger) { 77 return TraceIntelPTSessionFileParser(debugger, trace_session_file, 78 session_file_dir) 79 .Parse(); 80 } 81 82 Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) { 83 TraceSP instance(new TraceIntelPT(process)); 84 process.GetTarget().SetTrace(instance); 85 return instance; 86 } 87 88 TraceIntelPT::TraceIntelPT( 89 const pt_cpu &cpu_info, 90 const std::vector<ThreadPostMortemTraceSP> &traced_threads) 91 : m_cpu_info(cpu_info) { 92 for (const ThreadPostMortemTraceSP &thread : traced_threads) 93 m_thread_decoders.emplace( 94 thread->GetID(), 95 std::make_unique<PostMortemThreadDecoder>(thread, *this)); 96 } 97 98 DecodedThreadSP TraceIntelPT::Decode(Thread &thread) { 99 RefreshLiveProcessState(); 100 if (m_live_refresh_error.hasValue()) 101 return std::make_shared<DecodedThread>( 102 thread.shared_from_this(), 103 createStringError(inconvertibleErrorCode(), *m_live_refresh_error)); 104 105 auto it = m_thread_decoders.find(thread.GetID()); 106 if (it == m_thread_decoders.end()) 107 return std::make_shared<DecodedThread>( 108 thread.shared_from_this(), 109 createStringError(inconvertibleErrorCode(), "thread not traced")); 110 return it->second->Decode(); 111 } 112 113 lldb::TraceCursorUP TraceIntelPT::GetCursor(Thread &thread) { 114 return Decode(thread)->GetCursor(); 115 } 116 117 void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { 118 Optional<size_t> raw_size = GetRawTraceSize(thread); 119 s.Printf("\nthread #%u: tid = %" PRIu64, thread.GetIndexID(), thread.GetID()); 120 if (!raw_size) { 121 s.Printf(", not traced\n"); 122 return; 123 } 124 s.Printf("\n Raw trace size: %zu bytes\n", *raw_size); 125 return; 126 } 127 128 Optional<size_t> TraceIntelPT::GetRawTraceSize(Thread &thread) { 129 if (IsTraced(thread.GetID())) 130 return Decode(thread)->GetRawTraceSize(); 131 else 132 return None; 133 } 134 135 Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() { 136 Expected<std::vector<uint8_t>> cpu_info = GetLiveProcessBinaryData("cpuInfo"); 137 if (!cpu_info) 138 return cpu_info.takeError(); 139 140 int64_t cpu_family = -1; 141 int64_t model = -1; 142 int64_t stepping = -1; 143 std::string vendor_id; 144 145 StringRef rest(reinterpret_cast<const char *>(cpu_info->data()), 146 cpu_info->size()); 147 while (!rest.empty()) { 148 StringRef line; 149 std::tie(line, rest) = rest.split('\n'); 150 151 SmallVector<StringRef, 2> columns; 152 line.split(columns, StringRef(":"), -1, false); 153 154 if (columns.size() < 2) 155 continue; // continue searching 156 157 columns[1] = columns[1].trim(" "); 158 if (columns[0].contains("cpu family") && 159 columns[1].getAsInteger(10, cpu_family)) 160 continue; 161 162 else if (columns[0].contains("model") && columns[1].getAsInteger(10, model)) 163 continue; 164 165 else if (columns[0].contains("stepping") && 166 columns[1].getAsInteger(10, stepping)) 167 continue; 168 169 else if (columns[0].contains("vendor_id")) { 170 vendor_id = columns[1].str(); 171 if (!vendor_id.empty()) 172 continue; 173 } 174 175 if ((cpu_family != -1) && (model != -1) && (stepping != -1) && 176 (!vendor_id.empty())) { 177 return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown, 178 static_cast<uint16_t>(cpu_family), 179 static_cast<uint8_t>(model), 180 static_cast<uint8_t>(stepping)}; 181 } 182 } 183 return createStringError(inconvertibleErrorCode(), 184 "Failed parsing the target's /proc/cpuinfo file"); 185 } 186 187 Expected<pt_cpu> TraceIntelPT::GetCPUInfo() { 188 if (!m_cpu_info) { 189 if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess()) 190 m_cpu_info = *cpu_info; 191 else 192 return cpu_info.takeError(); 193 } 194 return *m_cpu_info; 195 } 196 197 Process *TraceIntelPT::GetLiveProcess() { return m_live_process; } 198 199 void TraceIntelPT::DoRefreshLiveProcessState( 200 Expected<TraceGetStateResponse> state) { 201 m_thread_decoders.clear(); 202 203 if (!state) { 204 m_live_refresh_error = toString(state.takeError()); 205 return; 206 } 207 208 for (const TraceThreadState &thread_state : state->tracedThreads) { 209 Thread &thread = 210 *m_live_process->GetThreadList().FindThreadByID(thread_state.tid); 211 m_thread_decoders.emplace( 212 thread_state.tid, std::make_unique<LiveThreadDecoder>(thread, *this)); 213 } 214 } 215 216 bool TraceIntelPT::IsTraced(lldb::tid_t tid) { 217 RefreshLiveProcessState(); 218 return m_thread_decoders.count(tid); 219 } 220 221 // The information here should match the description of the intel-pt section 222 // of the jLLDBTraceStart packet in the lldb/docs/lldb-gdb-remote.txt 223 // documentation file. Similarly, it should match the CLI help messages of the 224 // TraceIntelPTOptions.td file. 225 const char *TraceIntelPT::GetStartConfigurationHelp() { 226 return R"(Parameters: 227 228 Note: If a parameter is not specified, a default value will be used. 229 230 - int threadBufferSize (defaults to 4096 bytes): 231 [process and thread tracing] 232 Trace size in bytes per thread. It must be a power of 2 greater 233 than or equal to 4096 (2^12). The trace is circular keeping the 234 the most recent data. 235 236 - boolean enableTsc (default to false): 237 [process and thread tracing] 238 Whether to use enable TSC timestamps or not. This is supported on 239 all devices that support intel-pt. 240 241 - psbPeriod (defaults to null): 242 [process and thread tracing] 243 This value defines the period in which PSB packets will be generated. 244 A PSB packet is a synchronization packet that contains a TSC 245 timestamp and the current absolute instruction pointer. 246 247 This parameter can only be used if 248 249 /sys/bus/event_source/devices/intel_pt/caps/psb_cyc 250 251 is 1. Otherwise, the PSB period will be defined by the processor. 252 253 If supported, valid values for this period can be found in 254 255 /sys/bus/event_source/devices/intel_pt/caps/psb_periods 256 257 which contains a hexadecimal number, whose bits represent 258 valid values e.g. if bit 2 is set, then value 2 is valid. 259 260 The psb_period value is converted to the approximate number of 261 raw trace bytes between PSB packets as: 262 263 2 ^ (value + 11) 264 265 e.g. value 3 means 16KiB between PSB packets. Defaults to 0 if 266 supported. 267 268 - int processBufferSizeLimit (defaults to 500 MB): 269 [process tracing only] 270 Maximum total trace size per process in bytes. This limit applies 271 to the sum of the sizes of all thread traces of this process, 272 excluding the ones created explicitly with "thread tracing". 273 Whenever a thread is attempted to be traced due to this command 274 and the limit would be reached, the process is stopped with a 275 "processor trace" reason, so that the user can retrace the process 276 if needed.)"; 277 } 278 279 Error TraceIntelPT::Start(size_t thread_buffer_size, 280 size_t total_buffer_size_limit, bool enable_tsc, 281 Optional<size_t> psb_period) { 282 TraceIntelPTStartRequest request; 283 request.threadBufferSize = thread_buffer_size; 284 request.processBufferSizeLimit = total_buffer_size_limit; 285 request.enableTsc = enable_tsc; 286 request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; }); 287 request.type = GetPluginName().AsCString(); 288 return Trace::Start(toJSON(request)); 289 } 290 291 Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) { 292 size_t thread_buffer_size = kDefaultThreadBufferSize; 293 size_t process_buffer_size_limit = kDefaultProcessBufferSizeLimit; 294 bool enable_tsc = kDefaultEnableTscValue; 295 Optional<size_t> psb_period = kDefaultPsbPeriod; 296 297 if (configuration) { 298 if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { 299 dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size); 300 dict->GetValueForKeyAsInteger("processBufferSizeLimit", 301 process_buffer_size_limit); 302 dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); 303 dict->GetValueForKeyAsInteger("psbPeriod", psb_period); 304 } else { 305 return createStringError(inconvertibleErrorCode(), 306 "configuration object is not a dictionary"); 307 } 308 } 309 310 return Start(thread_buffer_size, process_buffer_size_limit, enable_tsc, 311 psb_period); 312 } 313 314 llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, 315 size_t thread_buffer_size, bool enable_tsc, 316 Optional<size_t> psb_period) { 317 TraceIntelPTStartRequest request; 318 request.threadBufferSize = thread_buffer_size; 319 request.enableTsc = enable_tsc; 320 request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; }); 321 request.type = GetPluginName().AsCString(); 322 request.tids.emplace(); 323 for (lldb::tid_t tid : tids) 324 request.tids->push_back(tid); 325 return Trace::Start(toJSON(request)); 326 } 327 328 Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, 329 StructuredData::ObjectSP configuration) { 330 size_t thread_buffer_size = kDefaultThreadBufferSize; 331 bool enable_tsc = kDefaultEnableTscValue; 332 Optional<size_t> psb_period = kDefaultPsbPeriod; 333 334 if (configuration) { 335 if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { 336 dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size); 337 dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); 338 dict->GetValueForKeyAsInteger("psbPeriod", psb_period); 339 } else { 340 return createStringError(inconvertibleErrorCode(), 341 "configuration object is not a dictionary"); 342 } 343 } 344 345 return Start(tids, thread_buffer_size, enable_tsc, psb_period); 346 } 347 348 Expected<std::vector<uint8_t>> 349 TraceIntelPT::GetLiveThreadBuffer(lldb::tid_t tid) { 350 return Trace::GetLiveThreadBinaryData(tid, "threadTraceBuffer"); 351 } 352