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