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 StringRef TraceIntelPT::GetSchema() { 53 return TraceIntelPTSessionFileParser::GetSchema(); 54 } 55 56 void TraceIntelPT::Dump(Stream *s) const {} 57 58 llvm::Error TraceIntelPT::SaveLiveTraceToDisk(FileSpec directory) { 59 RefreshLiveProcessState(); 60 return TraceIntelPTSessionSaver().SaveToDisk(*this, directory); 61 } 62 63 Expected<TraceSP> TraceIntelPT::CreateInstanceForSessionFile( 64 const json::Value &trace_session_file, StringRef session_file_dir, 65 Debugger &debugger) { 66 return TraceIntelPTSessionFileParser(debugger, trace_session_file, 67 session_file_dir) 68 .Parse(); 69 } 70 71 Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) { 72 TraceSP instance(new TraceIntelPT(process)); 73 process.GetTarget().SetTrace(instance); 74 return instance; 75 } 76 77 TraceIntelPT::TraceIntelPT( 78 const pt_cpu &cpu_info, 79 const std::vector<ThreadPostMortemTraceSP> &traced_threads) 80 : m_cpu_info(cpu_info) { 81 for (const ThreadPostMortemTraceSP &thread : traced_threads) { 82 m_thread_decoders.emplace(thread->GetID(), 83 std::make_unique<ThreadDecoder>(thread, *this)); 84 SetPostMortemThreadDataFile(thread->GetID(), "threadTraceBuffer", 85 thread->GetTraceFile()); 86 } 87 } 88 89 DecodedThreadSP TraceIntelPT::Decode(Thread &thread) { 90 RefreshLiveProcessState(); 91 if (m_live_refresh_error.hasValue()) 92 return std::make_shared<DecodedThread>( 93 thread.shared_from_this(), 94 createStringError(inconvertibleErrorCode(), *m_live_refresh_error)); 95 96 auto it = m_thread_decoders.find(thread.GetID()); 97 if (it == m_thread_decoders.end()) 98 return std::make_shared<DecodedThread>( 99 thread.shared_from_this(), 100 createStringError(inconvertibleErrorCode(), "thread not traced")); 101 return it->second->Decode(); 102 } 103 104 lldb::TraceCursorUP TraceIntelPT::GetCursor(Thread &thread) { 105 return Decode(thread)->GetCursor(); 106 } 107 108 void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { 109 lldb::tid_t tid = thread.GetID(); 110 s.Format("\nthread #{0}: tid = {1}", thread.GetIndexID(), thread.GetID()); 111 if (!IsTraced(tid)) { 112 s << ", not traced\n"; 113 return; 114 } 115 s << "\n"; 116 117 Expected<size_t> raw_size = GetRawTraceSize(thread); 118 if (!raw_size) { 119 s.Format(" {0}\n", toString(raw_size.takeError())); 120 return; 121 } 122 123 DecodedThreadSP decoded_trace_sp = Decode(thread); 124 size_t insn_len = decoded_trace_sp->GetInstructionsCount(); 125 size_t mem_used = decoded_trace_sp->CalculateApproximateMemoryUsage(); 126 127 s.Format(" Total number of instructions: {0}\n", insn_len); 128 129 s << "\n Memory usage:\n"; 130 s.Format(" Raw trace size: {0} KiB\n", *raw_size / 1024); 131 s.Format( 132 " Total approximate memory usage (excluding raw trace): {0:2} KiB\n", 133 (double)mem_used / 1024); 134 if (insn_len != 0) 135 s.Format(" Average memory usage per instruction (excluding raw trace): " 136 "{0:2} bytes\n", 137 (double)mem_used / insn_len); 138 139 s << "\n Timing:\n"; 140 GetTimer().ForThread(tid).ForEachTimedTask( 141 [&](const std::string &name, std::chrono::milliseconds duration) { 142 s.Format(" {0}: {1:2}s\n", name, duration.count() / 1000.0); 143 }); 144 145 s << "\n Errors:\n"; 146 const DecodedThread::LibiptErrors &tsc_errors = 147 decoded_trace_sp->GetTscErrors(); 148 s.Format(" Number of TSC decoding errors: {0}\n", tsc_errors.total_count); 149 for (const auto &error_message_to_count : tsc_errors.libipt_errors) { 150 s.Format(" {0}: {1}\n", error_message_to_count.first, 151 error_message_to_count.second); 152 } 153 } 154 155 llvm::Expected<size_t> TraceIntelPT::GetRawTraceSize(Thread &thread) { 156 size_t size; 157 auto callback = [&](llvm::ArrayRef<uint8_t> data) { 158 size = data.size(); 159 return Error::success(); 160 }; 161 if (Error err = OnThreadBufferRead(thread.GetID(), callback)) 162 return std::move(err); 163 164 return size; 165 } 166 167 Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() { 168 Expected<std::vector<uint8_t>> cpu_info = GetLiveProcessBinaryData("cpuInfo"); 169 if (!cpu_info) 170 return cpu_info.takeError(); 171 172 int64_t cpu_family = -1; 173 int64_t model = -1; 174 int64_t stepping = -1; 175 std::string vendor_id; 176 177 StringRef rest(reinterpret_cast<const char *>(cpu_info->data()), 178 cpu_info->size()); 179 while (!rest.empty()) { 180 StringRef line; 181 std::tie(line, rest) = rest.split('\n'); 182 183 SmallVector<StringRef, 2> columns; 184 line.split(columns, StringRef(":"), -1, false); 185 186 if (columns.size() < 2) 187 continue; // continue searching 188 189 columns[1] = columns[1].trim(" "); 190 if (columns[0].contains("cpu family") && 191 columns[1].getAsInteger(10, cpu_family)) 192 continue; 193 194 else if (columns[0].contains("model") && columns[1].getAsInteger(10, model)) 195 continue; 196 197 else if (columns[0].contains("stepping") && 198 columns[1].getAsInteger(10, stepping)) 199 continue; 200 201 else if (columns[0].contains("vendor_id")) { 202 vendor_id = columns[1].str(); 203 if (!vendor_id.empty()) 204 continue; 205 } 206 207 if ((cpu_family != -1) && (model != -1) && (stepping != -1) && 208 (!vendor_id.empty())) { 209 return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown, 210 static_cast<uint16_t>(cpu_family), 211 static_cast<uint8_t>(model), 212 static_cast<uint8_t>(stepping)}; 213 } 214 } 215 return createStringError(inconvertibleErrorCode(), 216 "Failed parsing the target's /proc/cpuinfo file"); 217 } 218 219 Expected<pt_cpu> TraceIntelPT::GetCPUInfo() { 220 if (!m_cpu_info) { 221 if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess()) 222 m_cpu_info = *cpu_info; 223 else 224 return cpu_info.takeError(); 225 } 226 return *m_cpu_info; 227 } 228 229 Process *TraceIntelPT::GetLiveProcess() { return m_live_process; } 230 231 void TraceIntelPT::DoRefreshLiveProcessState( 232 Expected<TraceGetStateResponse> state) { 233 m_thread_decoders.clear(); 234 235 if (!state) { 236 m_live_refresh_error = toString(state.takeError()); 237 return; 238 } 239 240 for (const TraceThreadState &thread_state : state->tracedThreads) { 241 ThreadSP thread_sp = 242 m_live_process->GetThreadList().FindThreadByID(thread_state.tid); 243 m_thread_decoders.emplace( 244 thread_state.tid, std::make_unique<ThreadDecoder>(thread_sp, *this)); 245 } 246 } 247 248 bool TraceIntelPT::IsTraced(lldb::tid_t tid) { 249 RefreshLiveProcessState(); 250 return m_thread_decoders.count(tid); 251 } 252 253 // The information here should match the description of the intel-pt section 254 // of the jLLDBTraceStart packet in the lldb/docs/lldb-gdb-remote.txt 255 // documentation file. Similarly, it should match the CLI help messages of the 256 // TraceIntelPTOptions.td file. 257 const char *TraceIntelPT::GetStartConfigurationHelp() { 258 return R"(Parameters: 259 260 Note: If a parameter is not specified, a default value will be used. 261 262 - int threadBufferSize (defaults to 4096 bytes): 263 [process and thread tracing] 264 Trace size in bytes per thread. It must be a power of 2 greater 265 than or equal to 4096 (2^12). The trace is circular keeping the 266 the most recent data. 267 268 - boolean enableTsc (default to false): 269 [process and thread tracing] 270 Whether to use enable TSC timestamps or not. This is supported on 271 all devices that support intel-pt. 272 273 - psbPeriod (defaults to null): 274 [process and thread tracing] 275 This value defines the period in which PSB packets will be generated. 276 A PSB packet is a synchronization packet that contains a TSC 277 timestamp and the current absolute instruction pointer. 278 279 This parameter can only be used if 280 281 /sys/bus/event_source/devices/intel_pt/caps/psb_cyc 282 283 is 1. Otherwise, the PSB period will be defined by the processor. 284 285 If supported, valid values for this period can be found in 286 287 /sys/bus/event_source/devices/intel_pt/caps/psb_periods 288 289 which contains a hexadecimal number, whose bits represent 290 valid values e.g. if bit 2 is set, then value 2 is valid. 291 292 The psb_period value is converted to the approximate number of 293 raw trace bytes between PSB packets as: 294 295 2 ^ (value + 11) 296 297 e.g. value 3 means 16KiB between PSB packets. Defaults to 0 if 298 supported. 299 300 - int processBufferSizeLimit (defaults to 500 MB): 301 [process tracing only] 302 Maximum total trace size per process in bytes. This limit applies 303 to the sum of the sizes of all thread traces of this process, 304 excluding the ones created explicitly with "thread tracing". 305 Whenever a thread is attempted to be traced due to this command 306 and the limit would be reached, the process is stopped with a 307 "processor trace" reason, so that the user can retrace the process 308 if needed.)"; 309 } 310 311 Error TraceIntelPT::Start(size_t thread_buffer_size, 312 size_t total_buffer_size_limit, bool enable_tsc, 313 Optional<size_t> psb_period) { 314 TraceIntelPTStartRequest request; 315 request.threadBufferSize = thread_buffer_size; 316 request.processBufferSizeLimit = total_buffer_size_limit; 317 request.enableTsc = enable_tsc; 318 request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; }); 319 request.type = GetPluginName().str(); 320 return Trace::Start(toJSON(request)); 321 } 322 323 Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) { 324 size_t thread_buffer_size = kDefaultThreadBufferSize; 325 size_t process_buffer_size_limit = kDefaultProcessBufferSizeLimit; 326 bool enable_tsc = kDefaultEnableTscValue; 327 Optional<size_t> psb_period = kDefaultPsbPeriod; 328 329 if (configuration) { 330 if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { 331 dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size); 332 dict->GetValueForKeyAsInteger("processBufferSizeLimit", 333 process_buffer_size_limit); 334 dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); 335 dict->GetValueForKeyAsInteger("psbPeriod", psb_period); 336 } else { 337 return createStringError(inconvertibleErrorCode(), 338 "configuration object is not a dictionary"); 339 } 340 } 341 342 return Start(thread_buffer_size, process_buffer_size_limit, enable_tsc, 343 psb_period); 344 } 345 346 llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, 347 size_t thread_buffer_size, bool enable_tsc, 348 Optional<size_t> psb_period) { 349 TraceIntelPTStartRequest request; 350 request.threadBufferSize = thread_buffer_size; 351 request.enableTsc = enable_tsc; 352 request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; }); 353 request.type = GetPluginName().str(); 354 request.tids.emplace(); 355 for (lldb::tid_t tid : tids) 356 request.tids->push_back(tid); 357 return Trace::Start(toJSON(request)); 358 } 359 360 Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, 361 StructuredData::ObjectSP configuration) { 362 size_t thread_buffer_size = kDefaultThreadBufferSize; 363 bool enable_tsc = kDefaultEnableTscValue; 364 Optional<size_t> psb_period = kDefaultPsbPeriod; 365 366 if (configuration) { 367 if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { 368 dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size); 369 dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); 370 dict->GetValueForKeyAsInteger("psbPeriod", psb_period); 371 } else { 372 return createStringError(inconvertibleErrorCode(), 373 "configuration object is not a dictionary"); 374 } 375 } 376 377 return Start(tids, thread_buffer_size, enable_tsc, psb_period); 378 } 379 380 Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid, 381 OnBinaryDataReadCallback callback) { 382 return OnThreadBinaryDataRead(tid, "threadTraceBuffer", callback); 383 } 384 385 TaskTimer &TraceIntelPT::GetTimer() { return m_task_timer; } 386