blob: f14ad38157804b06253e85eb2790c21960a3822f [file] [log] [blame]
Dean Michael Berris0e8abab2017-02-01 00:05:29 +00001//===- 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
25namespace llvm {
26namespace xray {
27
28Optional<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
35Optional<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
42namespace {
43Error 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
125Error 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.
157Expected<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