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