1 //===- InlineInfo.cpp -------------------------------------------*- C++ -*-===// 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 "llvm/DebugInfo/GSYM/FileEntry.h" 10 #include "llvm/DebugInfo/GSYM/FileWriter.h" 11 #include "llvm/DebugInfo/GSYM/GsymReader.h" 12 #include "llvm/DebugInfo/GSYM/InlineInfo.h" 13 #include "llvm/Support/DataExtractor.h" 14 #include <algorithm> 15 #include <inttypes.h> 16 17 using namespace llvm; 18 using namespace gsym; 19 20 21 raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const InlineInfo &II) { 22 if (!II.isValid()) 23 return OS; 24 bool First = true; 25 for (auto Range : II.Ranges) { 26 if (First) 27 First = false; 28 else 29 OS << ' '; 30 OS << Range; 31 } 32 OS << " Name = " << HEX32(II.Name) << ", CallFile = " << II.CallFile 33 << ", CallLine = " << II.CallFile << '\n'; 34 for (const auto &Child : II.Children) 35 OS << Child; 36 return OS; 37 } 38 39 static bool getInlineStackHelper(const InlineInfo &II, uint64_t Addr, 40 std::vector<const InlineInfo *> &InlineStack) { 41 if (II.Ranges.contains(Addr)) { 42 // If this is the top level that represents the concrete function, 43 // there will be no name and we shoud clear the inline stack. Otherwise 44 // we have found an inline call stack that we need to insert. 45 if (II.Name != 0) 46 InlineStack.insert(InlineStack.begin(), &II); 47 for (const auto &Child : II.Children) { 48 if (::getInlineStackHelper(Child, Addr, InlineStack)) 49 break; 50 } 51 return !InlineStack.empty(); 52 } 53 return false; 54 } 55 56 llvm::Optional<InlineInfo::InlineArray> InlineInfo::getInlineStack(uint64_t Addr) const { 57 InlineArray Result; 58 if (getInlineStackHelper(*this, Addr, Result)) 59 return Result; 60 return llvm::None; 61 } 62 63 /// Skip an InlineInfo object in the specified data at the specified offset. 64 /// 65 /// Used during the InlineInfo::lookup() call to quickly skip child InlineInfo 66 /// objects where the addres ranges isn't contained in the InlineInfo object 67 /// or its children. This avoids allocations by not appending child InlineInfo 68 /// objects to the InlineInfo::Children array. 69 /// 70 /// \param Data The binary stream to read the data from. 71 /// 72 /// \param Offset The byte offset within \a Data. 73 /// 74 /// \param SkippedRanges If true, address ranges have already been skipped. 75 76 static bool skip(DataExtractor &Data, uint64_t &Offset, bool SkippedRanges) { 77 if (!SkippedRanges) { 78 if (AddressRanges::skip(Data, Offset) == 0) 79 return false; 80 } 81 bool HasChildren = Data.getU8(&Offset) != 0; 82 Data.getU32(&Offset); // Skip Inline.Name. 83 Data.getULEB128(&Offset); // Skip Inline.CallFile. 84 Data.getULEB128(&Offset); // Skip Inline.CallLine. 85 if (HasChildren) { 86 while (skip(Data, Offset, false /* SkippedRanges */)) 87 /* Do nothing */; 88 } 89 // We skipped a valid InlineInfo. 90 return true; 91 } 92 93 /// A Lookup helper functions. 94 /// 95 /// Used during the InlineInfo::lookup() call to quickly only parse an 96 /// InlineInfo object if the address falls within this object. This avoids 97 /// allocations by not appending child InlineInfo objects to the 98 /// InlineInfo::Children array and also skips any InlineInfo objects that do 99 /// not contain the address we are looking up. 100 /// 101 /// \param Data The binary stream to read the data from. 102 /// 103 /// \param Offset The byte offset within \a Data. 104 /// 105 /// \param BaseAddr The address that the relative address range offsets are 106 /// relative to. 107 108 static bool lookup(const GsymReader &GR, DataExtractor &Data, uint64_t &Offset, 109 uint64_t BaseAddr, uint64_t Addr, SourceLocations &SrcLocs, 110 llvm::Error &Err) { 111 InlineInfo Inline; 112 Inline.Ranges.decode(Data, BaseAddr, Offset); 113 if (Inline.Ranges.empty()) 114 return true; 115 // Check if the address is contained within the inline information, and if 116 // not, quickly skip this InlineInfo object and all its children. 117 if (!Inline.Ranges.contains(Addr)) { 118 skip(Data, Offset, true /* SkippedRanges */); 119 return false; 120 } 121 122 // The address range is contained within this InlineInfo, add the source 123 // location for this InlineInfo and any children that contain the address. 124 bool HasChildren = Data.getU8(&Offset) != 0; 125 Inline.Name = Data.getU32(&Offset); 126 Inline.CallFile = (uint32_t)Data.getULEB128(&Offset); 127 Inline.CallLine = (uint32_t)Data.getULEB128(&Offset); 128 if (HasChildren) { 129 // Child address ranges are encoded relative to the first address in the 130 // parent InlineInfo object. 131 const auto ChildBaseAddr = Inline.Ranges[0].Start; 132 bool Done = false; 133 while (!Done) 134 Done = lookup(GR, Data, Offset, ChildBaseAddr, Addr, SrcLocs, Err); 135 } 136 137 Optional<FileEntry> CallFile = GR.getFile(Inline.CallFile); 138 if (!CallFile) { 139 Err = createStringError(std::errc::invalid_argument, 140 "failed to extract file[%" PRIu32 "]", 141 Inline.CallFile); 142 return false; 143 } 144 145 if (CallFile->Dir || CallFile->Base) { 146 SourceLocation SrcLoc; 147 SrcLoc.Name = SrcLocs.back().Name; 148 SrcLoc.Dir = GR.getString(CallFile->Dir); 149 SrcLoc.Base = GR.getString(CallFile->Base); 150 SrcLoc.Line = Inline.CallLine; 151 SrcLocs.back().Name = GR.getString(Inline.Name); 152 SrcLocs.push_back(SrcLoc); 153 } 154 return true; 155 } 156 157 llvm::Error InlineInfo::lookup(const GsymReader &GR, DataExtractor &Data, 158 uint64_t BaseAddr, uint64_t Addr, 159 SourceLocations &SrcLocs) { 160 // Call our recursive helper function starting at offset zero. 161 uint64_t Offset = 0; 162 llvm::Error Err = Error::success(); 163 ::lookup(GR, Data, Offset, BaseAddr, Addr, SrcLocs, Err); 164 return Err; 165 } 166 167 /// Decode an InlineInfo in Data at the specified offset. 168 /// 169 /// A local helper function to decode InlineInfo objects. This function is 170 /// called recursively when parsing child InlineInfo objects. 171 /// 172 /// \param Data The data extractor to decode from. 173 /// \param Offset The offset within \a Data to decode from. 174 /// \param BaseAddr The base address to use when decoding address ranges. 175 /// \returns An InlineInfo or an error describing the issue that was 176 /// encountered during decoding. 177 static llvm::Expected<InlineInfo> decode(DataExtractor &Data, uint64_t &Offset, 178 uint64_t BaseAddr) { 179 InlineInfo Inline; 180 if (!Data.isValidOffset(Offset)) 181 return createStringError(std::errc::io_error, 182 "0x%8.8" PRIx64 ": missing InlineInfo address ranges data", Offset); 183 Inline.Ranges.decode(Data, BaseAddr, Offset); 184 if (Inline.Ranges.empty()) 185 return Inline; 186 if (!Data.isValidOffsetForDataOfSize(Offset, 1)) 187 return createStringError(std::errc::io_error, 188 "0x%8.8" PRIx64 ": missing InlineInfo uint8_t indicating children", 189 Offset); 190 bool HasChildren = Data.getU8(&Offset) != 0; 191 if (!Data.isValidOffsetForDataOfSize(Offset, 4)) 192 return createStringError(std::errc::io_error, 193 "0x%8.8" PRIx64 ": missing InlineInfo uint32_t for name", Offset); 194 Inline.Name = Data.getU32(&Offset); 195 if (!Data.isValidOffset(Offset)) 196 return createStringError(std::errc::io_error, 197 "0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call file", Offset); 198 Inline.CallFile = (uint32_t)Data.getULEB128(&Offset); 199 if (!Data.isValidOffset(Offset)) 200 return createStringError(std::errc::io_error, 201 "0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call line", Offset); 202 Inline.CallLine = (uint32_t)Data.getULEB128(&Offset); 203 if (HasChildren) { 204 // Child address ranges are encoded relative to the first address in the 205 // parent InlineInfo object. 206 const auto ChildBaseAddr = Inline.Ranges[0].Start; 207 while (true) { 208 llvm::Expected<InlineInfo> Child = decode(Data, Offset, ChildBaseAddr); 209 if (!Child) 210 return Child.takeError(); 211 // InlineInfo with empty Ranges termintes a child sibling chain. 212 if (Child.get().Ranges.empty()) 213 break; 214 Inline.Children.emplace_back(std::move(*Child)); 215 } 216 } 217 return Inline; 218 } 219 220 llvm::Expected<InlineInfo> InlineInfo::decode(DataExtractor &Data, 221 uint64_t BaseAddr) { 222 uint64_t Offset = 0; 223 return ::decode(Data, Offset, BaseAddr); 224 } 225 226 llvm::Error InlineInfo::encode(FileWriter &O, uint64_t BaseAddr) const { 227 // Users must verify the InlineInfo is valid prior to calling this funtion. 228 // We don't want to emit any InlineInfo objects if they are not valid since 229 // it will waste space in the GSYM file. 230 if (!isValid()) 231 return createStringError(std::errc::invalid_argument, 232 "attempted to encode invalid InlineInfo object"); 233 Ranges.encode(O, BaseAddr); 234 bool HasChildren = !Children.empty(); 235 O.writeU8(HasChildren); 236 O.writeU32(Name); 237 O.writeULEB(CallFile); 238 O.writeULEB(CallLine); 239 if (HasChildren) { 240 // Child address ranges are encoded as relative to the first 241 // address in the Ranges for this object. This keeps the offsets 242 // small and allows for efficient encoding using ULEB offsets. 243 const uint64_t ChildBaseAddr = Ranges[0].Start; 244 for (const auto &Child : Children) { 245 // Make sure all child address ranges are contained in the parent address 246 // ranges. 247 for (const auto &ChildRange: Child.Ranges) { 248 if (!Ranges.contains(ChildRange)) 249 return createStringError(std::errc::invalid_argument, 250 "child range not contained in parent"); 251 } 252 llvm::Error Err = Child.encode(O, ChildBaseAddr); 253 if (Err) 254 return Err; 255 } 256 257 // Terminate child sibling chain by emitting a zero. This zero will cause 258 // the decodeAll() function above to return false and stop the decoding 259 // of child InlineInfo objects that are siblings. 260 O.writeULEB(0); 261 } 262 return Error::success(); 263 } 264