xref: /llvm-project/llvm/lib/XRay/InstrumentationMap.cpp (revision 0e8ababf7d20214267b320bd73003e82c99f1d0e)
1 //===- InstrumentationMap.cpp - XRay Instrumentation Map ------------------===//
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 // Implementation of the InstrumentationMap type for XRay sleds.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef XRAY_INSTRUMENTATIONMAP_H
15 #define XRAY_INSTRUMENTATIONMAP_H
16 
17 #include "llvm/XRay/InstrumentationMap.h"
18 
19 #include "llvm/Object/ObjectFile.h"
20 #include "llvm/Support/DataExtractor.h"
21 #include "llvm/Support/FileSystem.h"
22 #include "llvm/XRay/XRayRecord.h"
23 #include <system_error>
24 
25 namespace llvm {
26 namespace xray {
27 
28 Optional<int32_t> InstrumentationMap::getFunctionId(uint64_t Addr) const {
29   auto I = FunctionIds.find(Addr);
30   if (I != FunctionIds.end())
31     return I->second;
32   return None;
33 }
34 
35 Optional<uint64_t> InstrumentationMap::getFunctionAddr(int32_t FuncId) const {
36   auto I = FunctionAddresses.find(FuncId);
37   if (I != FunctionAddresses.end())
38     return I->second;
39   return None;
40 }
41 
42 namespace {
43 Error loadELF64(StringRef Filename,
44                 object::OwningBinary<object::ObjectFile> &ObjFile,
45                 InstrumentationMap::SledContainer &Sleds,
46                 InstrumentationMap::FunctionAddressMap &FunctionAddresses,
47                 InstrumentationMap::FunctionAddressReverseMap &FunctionIds) {
48   InstrumentationMap Map;
49 
50   // Find the section named "xray_instr_map".
51   if (!ObjFile.getBinary()->isELF() ||
52       ObjFile.getBinary()->getArch() != Triple::x86_64)
53     return make_error<StringError>(
54         "File format not supported (only does ELF little endian 64-bit).",
55         std::make_error_code(std::errc::not_supported));
56 
57   StringRef Contents = "";
58   const auto &Sections = ObjFile.getBinary()->sections();
59   auto I = find_if(Sections, [&](object::SectionRef Section) {
60     StringRef Name = "";
61     if (Section.getName(Name))
62       return false;
63     return Name == "xray_instr_map";
64   });
65 
66   if (I == Sections.end())
67     return make_error<StringError>(
68         "Failed to find XRay instrumentation map.",
69         std::make_error_code(std::errc::executable_format_error));
70 
71   if (I->getContents(Contents))
72     return errorCodeToError(
73         std::make_error_code(std::errc::executable_format_error));
74 
75   // Copy the instrumentation map data into the Sleds data structure.
76   auto C = Contents.bytes_begin();
77   static constexpr size_t ELF64SledEntrySize = 32;
78 
79   if ((C - Contents.bytes_end()) % ELF64SledEntrySize != 0)
80     return make_error<StringError>(
81         Twine("Instrumentation map entries not evenly divisible by size of "
82               "an XRay sled entry in ELF64."),
83         std::make_error_code(std::errc::executable_format_error));
84 
85   int32_t FuncId = 1;
86   uint64_t CurFn = 0;
87   for (; C != Contents.bytes_end(); C += ELF64SledEntrySize) {
88     DataExtractor Extractor(
89         StringRef(reinterpret_cast<const char *>(C), ELF64SledEntrySize), true,
90         8);
91     Sleds.push_back({});
92     auto &Entry = Sleds.back();
93     uint32_t OffsetPtr = 0;
94     Entry.Address = Extractor.getU64(&OffsetPtr);
95     Entry.Function = Extractor.getU64(&OffsetPtr);
96     auto Kind = Extractor.getU8(&OffsetPtr);
97     static constexpr SledEntry::FunctionKinds Kinds[] = {
98         SledEntry::FunctionKinds::ENTRY, SledEntry::FunctionKinds::EXIT,
99         SledEntry::FunctionKinds::TAIL,
100     };
101     if (Kind >= sizeof(Kinds))
102       return errorCodeToError(
103           std::make_error_code(std::errc::executable_format_error));
104     Entry.Kind = Kinds[Kind];
105     Entry.AlwaysInstrument = Extractor.getU8(&OffsetPtr) != 0;
106 
107     // We do replicate the function id generation scheme implemented in the
108     // XRay runtime.
109     // FIXME: Figure out how to keep this consistent with the XRay runtime.
110     if (CurFn == 0) {
111       CurFn = Entry.Function;
112       FunctionAddresses[FuncId] = Entry.Function;
113       FunctionIds[Entry.Function] = FuncId;
114     }
115     if (Entry.Function != CurFn) {
116       ++FuncId;
117       CurFn = Entry.Function;
118       FunctionAddresses[FuncId] = Entry.Function;
119       FunctionIds[Entry.Function] = FuncId;
120     }
121   }
122   return Error::success();
123 }
124 
125 Error loadYAML(int Fd, size_t FileSize, StringRef Filename,
126                InstrumentationMap::SledContainer &Sleds,
127                InstrumentationMap::FunctionAddressMap &FunctionAddresses,
128                InstrumentationMap::FunctionAddressReverseMap &FunctionIds) {
129   std::error_code EC;
130   sys::fs::mapped_file_region MappedFile(
131       Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC);
132   if (EC)
133     return make_error<StringError>(
134         Twine("Failed memory-mapping file '") + Filename + "'.", EC);
135 
136   std::vector<YAMLXRaySledEntry> YAMLSleds;
137   yaml::Input In(StringRef(MappedFile.data(), MappedFile.size()));
138   In >> YAMLSleds;
139   if (In.error())
140     return make_error<StringError>(
141         Twine("Failed loading YAML document from '") + Filename + "'.",
142         In.error());
143 
144   Sleds.reserve(YAMLSleds.size());
145   for (const auto &Y : YAMLSleds) {
146     FunctionAddresses[Y.FuncId] = Y.Function;
147     FunctionIds[Y.Function] = Y.FuncId;
148     Sleds.push_back(
149         SledEntry{Y.Address, Y.Function, Y.Kind, Y.AlwaysInstrument});
150   }
151   return Error::success();
152 }
153 } // namespace
154 
155 // FIXME: Create error types that encapsulate a bit more information than what
156 // StringError instances contain.
157 Expected<InstrumentationMap> loadInstrumentationMap(StringRef Filename) {
158   // At this point we assume the file is an object file -- and if that doesn't
159   // work, we treat it as YAML.
160   // FIXME: Extend to support non-ELF and non-x86_64 binaries.
161 
162   InstrumentationMap Map;
163   auto ObjectFileOrError = object::ObjectFile::createObjectFile(Filename);
164   if (!ObjectFileOrError) {
165     auto E = ObjectFileOrError.takeError();
166     // We try to load it as YAML if the ELF load didn't work.
167     int Fd;
168     if (sys::fs::openFileForRead(Filename, Fd))
169       return std::move(E);
170 
171     uint64_t FileSize;
172     if (sys::fs::file_size(Filename, FileSize))
173       return std::move(E);
174 
175     // If the file is empty, we return the original error.
176     if (FileSize == 0)
177       return std::move(E);
178 
179     // From this point on the errors will be only for the YAML parts, so we
180     // consume the errors at this point.
181     consumeError(std::move(E));
182     if (auto E = loadYAML(Fd, FileSize, Filename, Map.Sleds,
183                           Map.FunctionAddresses, Map.FunctionIds))
184       return std::move(E);
185   } else if (auto E = loadELF64(Filename, *ObjectFileOrError, Map.Sleds,
186                                 Map.FunctionAddresses, Map.FunctionIds)) {
187     return std::move(E);
188   }
189   return Map;
190 }
191 }
192 }
193 
194 #endif // XRAY_INSTRUMENTATIONMAP_H
195