1 //===-- IntelPTSingleBufferTrace.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 "IntelPTSingleBufferTrace.h" 10 11 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" 12 #include "lldb/Utility/Status.h" 13 #include "lldb/Utility/StreamString.h" 14 15 #include "llvm/Support/Host.h" 16 #include "llvm/Support/MemoryBuffer.h" 17 18 #include <sstream> 19 20 #include <linux/perf_event.h> 21 #include <sys/syscall.h> 22 #include <unistd.h> 23 24 using namespace lldb; 25 using namespace lldb_private; 26 using namespace process_linux; 27 using namespace llvm; 28 29 const char *kOSEventIntelPTTypeFile = 30 "/sys/bus/event_source/devices/intel_pt/type"; 31 32 const char *kPSBPeriodCapFile = 33 "/sys/bus/event_source/devices/intel_pt/caps/psb_cyc"; 34 35 const char *kPSBPeriodValidValuesFile = 36 "/sys/bus/event_source/devices/intel_pt/caps/psb_periods"; 37 38 const char *kPSBPeriodBitOffsetFile = 39 "/sys/bus/event_source/devices/intel_pt/format/psb_period"; 40 41 const char *kTSCBitOffsetFile = 42 "/sys/bus/event_source/devices/intel_pt/format/tsc"; 43 44 enum IntelPTConfigFileType { 45 Hex = 0, 46 // 0 or 1 47 ZeroOne, 48 Decimal, 49 // a bit index file always starts with the prefix config: following by an int, 50 // which represents the offset of the perf_event_attr.config value where to 51 // store a given configuration. 52 BitOffset 53 }; 54 55 static Expected<uint32_t> ReadIntelPTConfigFile(const char *file, 56 IntelPTConfigFileType type) { 57 ErrorOr<std::unique_ptr<MemoryBuffer>> stream = 58 MemoryBuffer::getFileAsStream(file); 59 60 if (!stream) 61 return createStringError(inconvertibleErrorCode(), 62 "Can't open the file '%s'", file); 63 64 uint32_t value = 0; 65 StringRef text_buffer = stream.get()->getBuffer(); 66 67 if (type == BitOffset) { 68 const char *prefix = "config:"; 69 if (!text_buffer.startswith(prefix)) 70 return createStringError(inconvertibleErrorCode(), 71 "The file '%s' contents doesn't start with '%s'", 72 file, prefix); 73 text_buffer = text_buffer.substr(strlen(prefix)); 74 } 75 76 auto getRadix = [&]() { 77 switch (type) { 78 case Hex: 79 return 16; 80 case ZeroOne: 81 case Decimal: 82 case BitOffset: 83 return 10; 84 } 85 llvm_unreachable("Fully covered switch above!"); 86 }; 87 88 auto createError = [&](const char *expected_value_message) { 89 return createStringError( 90 inconvertibleErrorCode(), 91 "The file '%s' has an invalid value. It should be %s.", file, 92 expected_value_message); 93 }; 94 95 if (text_buffer.trim().consumeInteger(getRadix(), value) || 96 (type == ZeroOne && value != 0 && value != 1)) { 97 switch (type) { 98 case Hex: 99 return createError("an unsigned hexadecimal int"); 100 case ZeroOne: 101 return createError("0 or 1"); 102 case Decimal: 103 case BitOffset: 104 return createError("an unsigned decimal int"); 105 } 106 } 107 return value; 108 } 109 110 /// Return the Linux perf event type for Intel PT. 111 Expected<uint32_t> process_linux::GetIntelPTOSEventType() { 112 return ReadIntelPTConfigFile(kOSEventIntelPTTypeFile, 113 IntelPTConfigFileType::Decimal); 114 } 115 116 static Error CheckPsbPeriod(size_t psb_period) { 117 Expected<uint32_t> cap = 118 ReadIntelPTConfigFile(kPSBPeriodCapFile, IntelPTConfigFileType::ZeroOne); 119 if (!cap) 120 return cap.takeError(); 121 if (*cap == 0) 122 return createStringError(inconvertibleErrorCode(), 123 "psb_period is unsupported in the system."); 124 125 Expected<uint32_t> valid_values = ReadIntelPTConfigFile( 126 kPSBPeriodValidValuesFile, IntelPTConfigFileType::Hex); 127 if (!valid_values) 128 return valid_values.takeError(); 129 130 if (valid_values.get() & (1 << psb_period)) 131 return Error::success(); 132 133 std::ostringstream error; 134 // 0 is always a valid value 135 error << "Invalid psb_period. Valid values are: 0"; 136 uint32_t mask = valid_values.get(); 137 while (mask) { 138 int index = __builtin_ctz(mask); 139 if (index > 0) 140 error << ", " << index; 141 // clear the lowest bit 142 mask &= mask - 1; 143 } 144 error << "."; 145 return createStringError(inconvertibleErrorCode(), error.str().c_str()); 146 } 147 148 #ifdef PERF_ATTR_SIZE_VER5 149 static Expected<uint64_t> 150 GeneratePerfEventConfigValue(bool enable_tsc, Optional<uint64_t> psb_period) { 151 uint64_t config = 0; 152 // tsc is always supported 153 if (enable_tsc) { 154 if (Expected<uint32_t> offset = ReadIntelPTConfigFile( 155 kTSCBitOffsetFile, IntelPTConfigFileType::BitOffset)) 156 config |= 1 << *offset; 157 else 158 return offset.takeError(); 159 } 160 if (psb_period) { 161 if (Error error = CheckPsbPeriod(*psb_period)) 162 return std::move(error); 163 164 if (Expected<uint32_t> offset = ReadIntelPTConfigFile( 165 kPSBPeriodBitOffsetFile, IntelPTConfigFileType::BitOffset)) 166 config |= *psb_period << *offset; 167 else 168 return offset.takeError(); 169 } 170 return config; 171 } 172 173 /// Create a \a perf_event_attr configured for 174 /// an IntelPT event. 175 /// 176 /// \return 177 /// A \a perf_event_attr if successful, 178 /// or an \a llvm::Error otherwise. 179 static Expected<perf_event_attr> 180 CreateIntelPTPerfEventConfiguration(bool enable_tsc, 181 llvm::Optional<uint64_t> psb_period) { 182 perf_event_attr attr; 183 memset(&attr, 0, sizeof(attr)); 184 attr.size = sizeof(attr); 185 attr.exclude_kernel = 1; 186 attr.exclude_hv = 1; 187 attr.exclude_idle = 1; 188 189 if (Expected<uint64_t> config_value = 190 GeneratePerfEventConfigValue(enable_tsc, psb_period)) 191 attr.config = *config_value; 192 else 193 return config_value.takeError(); 194 195 if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) 196 attr.type = *intel_pt_type; 197 else 198 return intel_pt_type.takeError(); 199 200 return attr; 201 } 202 #endif 203 204 size_t IntelPTSingleBufferTrace::GetIptTraceSize() const { 205 return m_perf_event.GetAuxBuffer().size(); 206 } 207 208 Error IntelPTSingleBufferTrace::Pause() { 209 return m_perf_event.DisableWithIoctl(); 210 } 211 212 Error IntelPTSingleBufferTrace::Resume() { 213 return m_perf_event.EnableWithIoctl(); 214 } 215 216 Expected<std::vector<uint8_t>> IntelPTSingleBufferTrace::GetIptTrace() { 217 // Disable the perf event to force a flush out of the CPU's internal buffer. 218 // Besides, we can guarantee that the CPU won't override any data as we are 219 // reading the buffer. 220 // The Intel documentation says: 221 // 222 // Packets are first buffered internally and then written out 223 // asynchronously. To collect packet output for postprocessing, a collector 224 // needs first to ensure that all packet data has been flushed from internal 225 // buffers. Software can ensure this by stopping packet generation by 226 // clearing IA32_RTIT_CTL.TraceEn (see “Disabling Packet Generation” in 227 // Section 35.2.7.2). 228 // 229 // This is achieved by the PERF_EVENT_IOC_DISABLE ioctl request, as 230 // mentioned in the man page of perf_event_open. 231 return m_perf_event.GetReadOnlyAuxBuffer(); 232 } 233 234 Expected<IntelPTSingleBufferTrace> IntelPTSingleBufferTrace::Start( 235 const TraceIntelPTStartRequest &request, Optional<lldb::tid_t> tid, 236 Optional<cpu_id_t> cpu_id, bool disabled, Optional<int> cgroup_fd) { 237 #ifndef PERF_ATTR_SIZE_VER5 238 return createStringError(inconvertibleErrorCode(), 239 "Intel PT Linux perf event not supported"); 240 #else 241 Log *log = GetLog(POSIXLog::Trace); 242 243 LLDB_LOG(log, "Will start tracing thread id {0} and cpu id {1}", tid, cpu_id); 244 245 if (__builtin_popcount(request.ipt_trace_size) != 1 || 246 request.ipt_trace_size < 4096) { 247 return createStringError( 248 inconvertibleErrorCode(), 249 "The intel pt trace size must be a power of 2 greater than or equal to " 250 "4096 (2^12) bytes. It was %" PRIu64 ".", 251 request.ipt_trace_size); 252 } 253 uint64_t page_size = getpagesize(); 254 uint64_t aux_buffer_numpages = static_cast<uint64_t>(llvm::PowerOf2Floor( 255 (request.ipt_trace_size + page_size - 1) / page_size)); 256 257 Expected<perf_event_attr> attr = CreateIntelPTPerfEventConfiguration( 258 request.enable_tsc, request.psb_period.map([](int value) { 259 return static_cast<uint64_t>(value); 260 })); 261 if (!attr) 262 return attr.takeError(); 263 attr->disabled = disabled; 264 265 LLDB_LOG(log, "Will create intel pt trace buffer of size {0}", 266 request.ipt_trace_size); 267 unsigned long flags = 0; 268 if (cgroup_fd) { 269 tid = *cgroup_fd; 270 flags |= PERF_FLAG_PID_CGROUP; 271 } 272 273 if (Expected<PerfEvent> perf_event = 274 PerfEvent::Init(*attr, tid, cpu_id, -1, flags)) { 275 if (Error mmap_err = perf_event->MmapMetadataAndBuffers( 276 /*num_data_pages=*/0, aux_buffer_numpages, 277 /*data_buffer_write=*/true)) { 278 return std::move(mmap_err); 279 } 280 return IntelPTSingleBufferTrace(std::move(*perf_event)); 281 } else { 282 return perf_event.takeError(); 283 } 284 #endif 285 } 286 287 const PerfEvent &IntelPTSingleBufferTrace::GetPerfEvent() const { 288 return m_perf_event; 289 } 290