xref: /freebsd/contrib/llvm-project/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp (revision a7dea1671b87c07d2d266f836bfa8b58efc7c134)
1 //===- InlineInfo.cpp -------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "llvm/DebugInfo/GSYM/FileEntry.h"
11 #include "llvm/DebugInfo/GSYM/FileWriter.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 /// Decode an InlineInfo in Data at the specified offset.
64 ///
65 /// A local helper function to decode InlineInfo objects. This function is
66 /// called recursively when parsing child InlineInfo objects.
67 ///
68 /// \param Data The data extractor to decode from.
69 /// \param Offset The offset within \a Data to decode from.
70 /// \param BaseAddr The base address to use when decoding address ranges.
71 /// \returns An InlineInfo or an error describing the issue that was
72 /// encountered during decoding.
73 static llvm::Expected<InlineInfo> decode(DataExtractor &Data, uint64_t &Offset,
74                                          uint64_t BaseAddr) {
75   InlineInfo Inline;
76   if (!Data.isValidOffset(Offset))
77     return createStringError(std::errc::io_error,
78         "0x%8.8" PRIx64 ": missing InlineInfo address ranges data", Offset);
79   Inline.Ranges.decode(Data, BaseAddr, Offset);
80   if (Inline.Ranges.empty())
81     return Inline;
82   if (!Data.isValidOffsetForDataOfSize(Offset, 1))
83     return createStringError(std::errc::io_error,
84         "0x%8.8" PRIx64 ": missing InlineInfo uint8_t indicating children",
85         Offset);
86   bool HasChildren = Data.getU8(&Offset) != 0;
87   if (!Data.isValidOffsetForDataOfSize(Offset, 4))
88     return createStringError(std::errc::io_error,
89         "0x%8.8" PRIx64 ": missing InlineInfo uint32_t for name", Offset);
90   Inline.Name = Data.getU32(&Offset);
91   if (!Data.isValidOffset(Offset))
92     return createStringError(std::errc::io_error,
93         "0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call file", Offset);
94   Inline.CallFile = (uint32_t)Data.getULEB128(&Offset);
95   if (!Data.isValidOffset(Offset))
96     return createStringError(std::errc::io_error,
97         "0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call line", Offset);
98   Inline.CallLine = (uint32_t)Data.getULEB128(&Offset);
99   if (HasChildren) {
100     // Child address ranges are encoded relative to the first address in the
101     // parent InlineInfo object.
102     const auto ChildBaseAddr = Inline.Ranges[0].Start;
103     while (true) {
104       llvm::Expected<InlineInfo> Child = decode(Data, Offset, ChildBaseAddr);
105       if (!Child)
106         return Child.takeError();
107       // InlineInfo with empty Ranges termintes a child sibling chain.
108       if (Child.get().Ranges.empty())
109         break;
110       Inline.Children.emplace_back(std::move(*Child));
111     }
112   }
113   return Inline;
114 }
115 
116 llvm::Expected<InlineInfo> InlineInfo::decode(DataExtractor &Data,
117                                               uint64_t BaseAddr) {
118   uint64_t Offset = 0;
119   return ::decode(Data, Offset, BaseAddr);
120 }
121 
122 llvm::Error InlineInfo::encode(FileWriter &O, uint64_t BaseAddr) const {
123   // Users must verify the InlineInfo is valid prior to calling this funtion.
124   // We don't want to emit any InlineInfo objects if they are not valid since
125   // it will waste space in the GSYM file.
126   if (!isValid())
127     return createStringError(std::errc::invalid_argument,
128                              "attempted to encode invalid InlineInfo object");
129   Ranges.encode(O, BaseAddr);
130   bool HasChildren = !Children.empty();
131   O.writeU8(HasChildren);
132   O.writeU32(Name);
133   O.writeULEB(CallFile);
134   O.writeULEB(CallLine);
135   if (HasChildren) {
136     // Child address ranges are encoded as relative to the first
137     // address in the Ranges for this object. This keeps the offsets
138     // small and allows for efficient encoding using ULEB offsets.
139     const uint64_t ChildBaseAddr = Ranges[0].Start;
140     for (const auto &Child : Children) {
141       // Make sure all child address ranges are contained in the parent address
142       // ranges.
143       for (const auto &ChildRange: Child.Ranges) {
144         if (!Ranges.contains(ChildRange))
145           return createStringError(std::errc::invalid_argument,
146                                    "child range not contained in parent");
147       }
148       llvm::Error Err = Child.encode(O, ChildBaseAddr);
149       if (Err)
150         return Err;
151     }
152 
153     // Terminate child sibling chain by emitting a zero. This zero will cause
154     // the decodeAll() function above to return false and stop the decoding
155     // of child InlineInfo objects that are siblings.
156     O.writeULEB(0);
157   }
158   return Error::success();
159 }
160