1 | //===-- llvm/Debuginfod/Debuginfod.cpp - Debuginfod client library --------===// |
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 | /// \file |
10 | /// |
11 | /// This file contains several definitions for the debuginfod client and server. |
12 | /// For the client, this file defines the fetchInfo function. For the server, |
13 | /// this file defines the DebuginfodLogEntry and DebuginfodServer structs, as |
14 | /// well as the DebuginfodLog, DebuginfodCollection classes. The fetchInfo |
15 | /// function retrieves any of the three supported artifact types: (executable, |
16 | /// debuginfo, source file) associated with a build-id from debuginfod servers. |
17 | /// If a source file is to be fetched, its absolute path must be specified in |
18 | /// the Description argument to fetchInfo. The DebuginfodLogEntry, |
19 | /// DebuginfodLog, and DebuginfodCollection are used by the DebuginfodServer to |
20 | /// scan the local filesystem for binaries and serve the debuginfod protocol. |
21 | /// |
22 | //===----------------------------------------------------------------------===// |
23 | |
24 | #include "llvm/Debuginfod/Debuginfod.h" |
25 | #include "llvm/ADT/StringExtras.h" |
26 | #include "llvm/ADT/StringRef.h" |
27 | #include "llvm/BinaryFormat/Magic.h" |
28 | #include "llvm/DebugInfo/DWARF/DWARFContext.h" |
29 | #include "llvm/DebugInfo/Symbolize/Symbolize.h" |
30 | #include "llvm/Debuginfod/HTTPClient.h" |
31 | #include "llvm/Object/BuildID.h" |
32 | #include "llvm/Object/ELFObjectFile.h" |
33 | #include "llvm/Support/CachePruning.h" |
34 | #include "llvm/Support/Caching.h" |
35 | #include "llvm/Support/Errc.h" |
36 | #include "llvm/Support/Error.h" |
37 | #include "llvm/Support/FileUtilities.h" |
38 | #include "llvm/Support/MemoryBuffer.h" |
39 | #include "llvm/Support/Path.h" |
40 | #include "llvm/Support/ThreadPool.h" |
41 | #include "llvm/Support/xxhash.h" |
42 | |
43 | #include <atomic> |
44 | #include <optional> |
45 | #include <thread> |
46 | |
47 | namespace llvm { |
48 | |
49 | using llvm::object::BuildIDRef; |
50 | |
51 | namespace { |
52 | std::optional<SmallVector<StringRef>> DebuginfodUrls; |
53 | // Many Readers/Single Writer lock protecting the global debuginfod URL list. |
54 | llvm::sys::RWMutex UrlsMutex; |
55 | } // namespace |
56 | |
57 | std::string getDebuginfodCacheKey(llvm::StringRef S) { |
58 | return utostr(X: xxh3_64bits(data: S)); |
59 | } |
60 | |
61 | // Returns a binary BuildID as a normalized hex string. |
62 | // Uses lowercase for compatibility with common debuginfod servers. |
63 | static std::string buildIDToString(BuildIDRef ID) { |
64 | return llvm::toHex(Input: ID, /*LowerCase=*/true); |
65 | } |
66 | |
67 | bool canUseDebuginfod() { |
68 | return HTTPClient::isAvailable() && !getDefaultDebuginfodUrls().empty(); |
69 | } |
70 | |
71 | SmallVector<StringRef> getDefaultDebuginfodUrls() { |
72 | std::shared_lock<llvm::sys::RWMutex> ReadGuard(UrlsMutex); |
73 | if (!DebuginfodUrls) { |
74 | // Only read from the environment variable if the user hasn't already |
75 | // set the value. |
76 | ReadGuard.unlock(); |
77 | std::unique_lock<llvm::sys::RWMutex> WriteGuard(UrlsMutex); |
78 | DebuginfodUrls = SmallVector<StringRef>(); |
79 | if (const char *DebuginfodUrlsEnv = std::getenv(name: "DEBUGINFOD_URLS" )) { |
80 | StringRef(DebuginfodUrlsEnv) |
81 | .split(A&: DebuginfodUrls.value(), Separator: " " , MaxSplit: -1, KeepEmpty: false); |
82 | } |
83 | WriteGuard.unlock(); |
84 | ReadGuard.lock(); |
85 | } |
86 | return DebuginfodUrls.value(); |
87 | } |
88 | |
89 | // Set the default debuginfod URL list, override the environment variable. |
90 | void setDefaultDebuginfodUrls(const SmallVector<StringRef> &URLs) { |
91 | std::unique_lock<llvm::sys::RWMutex> WriteGuard(UrlsMutex); |
92 | DebuginfodUrls = URLs; |
93 | } |
94 | |
95 | /// Finds a default local file caching directory for the debuginfod client, |
96 | /// first checking DEBUGINFOD_CACHE_PATH. |
97 | Expected<std::string> getDefaultDebuginfodCacheDirectory() { |
98 | if (const char *CacheDirectoryEnv = std::getenv(name: "DEBUGINFOD_CACHE_PATH" )) |
99 | return CacheDirectoryEnv; |
100 | |
101 | SmallString<64> CacheDirectory; |
102 | if (!sys::path::cache_directory(result&: CacheDirectory)) |
103 | return createStringError( |
104 | EC: errc::io_error, S: "Unable to determine appropriate cache directory." ); |
105 | sys::path::append(path&: CacheDirectory, a: "llvm-debuginfod" , b: "client" ); |
106 | return std::string(CacheDirectory); |
107 | } |
108 | |
109 | std::chrono::milliseconds getDefaultDebuginfodTimeout() { |
110 | long Timeout; |
111 | const char *DebuginfodTimeoutEnv = std::getenv(name: "DEBUGINFOD_TIMEOUT" ); |
112 | if (DebuginfodTimeoutEnv && |
113 | to_integer(S: StringRef(DebuginfodTimeoutEnv).trim(), Num&: Timeout, Base: 10)) |
114 | return std::chrono::milliseconds(Timeout * 1000); |
115 | |
116 | return std::chrono::milliseconds(90 * 1000); |
117 | } |
118 | |
119 | /// The following functions fetch a debuginfod artifact to a file in a local |
120 | /// cache and return the cached file path. They first search the local cache, |
121 | /// followed by the debuginfod servers. |
122 | |
123 | std::string getDebuginfodSourceUrlPath(BuildIDRef ID, |
124 | StringRef SourceFilePath) { |
125 | SmallString<64> UrlPath; |
126 | sys::path::append(path&: UrlPath, style: sys::path::Style::posix, a: "buildid" , |
127 | b: buildIDToString(ID), c: "source" , |
128 | d: sys::path::convert_to_slash(path: SourceFilePath)); |
129 | return std::string(UrlPath); |
130 | } |
131 | |
132 | Expected<std::string> getCachedOrDownloadSource(BuildIDRef ID, |
133 | StringRef SourceFilePath) { |
134 | std::string UrlPath = getDebuginfodSourceUrlPath(ID, SourceFilePath); |
135 | return getCachedOrDownloadArtifact(UniqueKey: getDebuginfodCacheKey(S: UrlPath), UrlPath); |
136 | } |
137 | |
138 | std::string getDebuginfodExecutableUrlPath(BuildIDRef ID) { |
139 | SmallString<64> UrlPath; |
140 | sys::path::append(path&: UrlPath, style: sys::path::Style::posix, a: "buildid" , |
141 | b: buildIDToString(ID), c: "executable" ); |
142 | return std::string(UrlPath); |
143 | } |
144 | |
145 | Expected<std::string> getCachedOrDownloadExecutable(BuildIDRef ID) { |
146 | std::string UrlPath = getDebuginfodExecutableUrlPath(ID); |
147 | return getCachedOrDownloadArtifact(UniqueKey: getDebuginfodCacheKey(S: UrlPath), UrlPath); |
148 | } |
149 | |
150 | std::string getDebuginfodDebuginfoUrlPath(BuildIDRef ID) { |
151 | SmallString<64> UrlPath; |
152 | sys::path::append(path&: UrlPath, style: sys::path::Style::posix, a: "buildid" , |
153 | b: buildIDToString(ID), c: "debuginfo" ); |
154 | return std::string(UrlPath); |
155 | } |
156 | |
157 | Expected<std::string> getCachedOrDownloadDebuginfo(BuildIDRef ID) { |
158 | std::string UrlPath = getDebuginfodDebuginfoUrlPath(ID); |
159 | return getCachedOrDownloadArtifact(UniqueKey: getDebuginfodCacheKey(S: UrlPath), UrlPath); |
160 | } |
161 | |
162 | // General fetching function. |
163 | Expected<std::string> getCachedOrDownloadArtifact(StringRef UniqueKey, |
164 | StringRef UrlPath) { |
165 | SmallString<10> CacheDir; |
166 | |
167 | Expected<std::string> CacheDirOrErr = getDefaultDebuginfodCacheDirectory(); |
168 | if (!CacheDirOrErr) |
169 | return CacheDirOrErr.takeError(); |
170 | CacheDir = *CacheDirOrErr; |
171 | |
172 | return getCachedOrDownloadArtifact(UniqueKey, UrlPath, CacheDirectoryPath: CacheDir, |
173 | DebuginfodUrls: getDefaultDebuginfodUrls(), |
174 | Timeout: getDefaultDebuginfodTimeout()); |
175 | } |
176 | |
177 | namespace { |
178 | |
179 | /// A simple handler which streams the returned data to a cache file. The cache |
180 | /// file is only created if a 200 OK status is observed. |
181 | class StreamedHTTPResponseHandler : public HTTPResponseHandler { |
182 | using CreateStreamFn = |
183 | std::function<Expected<std::unique_ptr<CachedFileStream>>()>; |
184 | CreateStreamFn CreateStream; |
185 | HTTPClient &Client; |
186 | std::unique_ptr<CachedFileStream> FileStream; |
187 | |
188 | public: |
189 | StreamedHTTPResponseHandler(CreateStreamFn CreateStream, HTTPClient &Client) |
190 | : CreateStream(CreateStream), Client(Client) {} |
191 | virtual ~StreamedHTTPResponseHandler() = default; |
192 | |
193 | Error handleBodyChunk(StringRef BodyChunk) override; |
194 | }; |
195 | |
196 | } // namespace |
197 | |
198 | Error StreamedHTTPResponseHandler::handleBodyChunk(StringRef BodyChunk) { |
199 | if (!FileStream) { |
200 | unsigned Code = Client.responseCode(); |
201 | if (Code && Code != 200) |
202 | return Error::success(); |
203 | Expected<std::unique_ptr<CachedFileStream>> FileStreamOrError = |
204 | CreateStream(); |
205 | if (!FileStreamOrError) |
206 | return FileStreamOrError.takeError(); |
207 | FileStream = std::move(*FileStreamOrError); |
208 | } |
209 | *FileStream->OS << BodyChunk; |
210 | return Error::success(); |
211 | } |
212 | |
213 | // An over-accepting simplification of the HTTP RFC 7230 spec. |
214 | static bool (StringRef S) { |
215 | StringRef Name; |
216 | StringRef Value; |
217 | std::tie(args&: Name, args&: Value) = S.split(Separator: ':'); |
218 | if (Name.empty() || Value.empty()) |
219 | return false; |
220 | return all_of(Range&: Name, P: [](char C) { return llvm::isPrint(C) && C != ' '; }) && |
221 | all_of(Range&: Value, P: [](char C) { return llvm::isPrint(C) || C == '\t'; }); |
222 | } |
223 | |
224 | static SmallVector<std::string, 0> () { |
225 | const char *Filename = getenv(name: "DEBUGINFOD_HEADERS_FILE" ); |
226 | if (!Filename) |
227 | return {}; |
228 | ErrorOr<std::unique_ptr<MemoryBuffer>> = |
229 | MemoryBuffer::getFile(Filename, /*IsText=*/true); |
230 | if (!HeadersFile) |
231 | return {}; |
232 | |
233 | SmallVector<std::string, 0> ; |
234 | uint64_t LineNumber = 0; |
235 | for (StringRef Line : llvm::split(Str: (*HeadersFile)->getBuffer(), Separator: '\n')) { |
236 | LineNumber++; |
237 | if (!Line.empty() && Line.back() == '\r') |
238 | Line = Line.drop_back(); |
239 | if (!isHeader(S: Line)) { |
240 | if (!all_of(Range&: Line, P: llvm::isSpace)) |
241 | WithColor::warning() |
242 | << "could not parse debuginfod header: " << Filename << ':' |
243 | << LineNumber << '\n'; |
244 | continue; |
245 | } |
246 | Headers.emplace_back(Args&: Line); |
247 | } |
248 | return Headers; |
249 | } |
250 | |
251 | Expected<std::string> getCachedOrDownloadArtifact( |
252 | StringRef UniqueKey, StringRef UrlPath, StringRef CacheDirectoryPath, |
253 | ArrayRef<StringRef> DebuginfodUrls, std::chrono::milliseconds Timeout) { |
254 | SmallString<64> AbsCachedArtifactPath; |
255 | sys::path::append(path&: AbsCachedArtifactPath, a: CacheDirectoryPath, |
256 | b: "llvmcache-" + UniqueKey); |
257 | |
258 | Expected<FileCache> CacheOrErr = |
259 | localCache(CacheNameRef: "Debuginfod-client" , TempFilePrefixRef: ".debuginfod-client" , CacheDirectoryPathRef: CacheDirectoryPath); |
260 | if (!CacheOrErr) |
261 | return CacheOrErr.takeError(); |
262 | |
263 | FileCache Cache = *CacheOrErr; |
264 | // We choose an arbitrary Task parameter as we do not make use of it. |
265 | unsigned Task = 0; |
266 | Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, UniqueKey, "" ); |
267 | if (!CacheAddStreamOrErr) |
268 | return CacheAddStreamOrErr.takeError(); |
269 | AddStreamFn &CacheAddStream = *CacheAddStreamOrErr; |
270 | if (!CacheAddStream) |
271 | return std::string(AbsCachedArtifactPath); |
272 | // The artifact was not found in the local cache, query the debuginfod |
273 | // servers. |
274 | if (!HTTPClient::isAvailable()) |
275 | return createStringError(EC: errc::io_error, |
276 | S: "No working HTTP client is available." ); |
277 | |
278 | if (!HTTPClient::IsInitialized) |
279 | return createStringError( |
280 | EC: errc::io_error, |
281 | S: "A working HTTP client is available, but it is not initialized. To " |
282 | "allow Debuginfod to make HTTP requests, call HTTPClient::initialize() " |
283 | "at the beginning of main." ); |
284 | |
285 | HTTPClient Client; |
286 | Client.setTimeout(Timeout); |
287 | for (StringRef ServerUrl : DebuginfodUrls) { |
288 | SmallString<64> ArtifactUrl; |
289 | sys::path::append(path&: ArtifactUrl, style: sys::path::Style::posix, a: ServerUrl, b: UrlPath); |
290 | |
291 | // Perform the HTTP request and if successful, write the response body to |
292 | // the cache. |
293 | { |
294 | StreamedHTTPResponseHandler Handler( |
295 | [&]() { return CacheAddStream(Task, "" ); }, Client); |
296 | HTTPRequest Request(ArtifactUrl); |
297 | Request.Headers = getHeaders(); |
298 | Error Err = Client.perform(Request, Handler); |
299 | if (Err) |
300 | return std::move(Err); |
301 | |
302 | unsigned Code = Client.responseCode(); |
303 | if (Code && Code != 200) |
304 | continue; |
305 | } |
306 | |
307 | Expected<CachePruningPolicy> PruningPolicyOrErr = |
308 | parseCachePruningPolicy(PolicyStr: std::getenv(name: "DEBUGINFOD_CACHE_POLICY" )); |
309 | if (!PruningPolicyOrErr) |
310 | return PruningPolicyOrErr.takeError(); |
311 | pruneCache(Path: CacheDirectoryPath, Policy: *PruningPolicyOrErr); |
312 | |
313 | // Return the path to the artifact on disk. |
314 | return std::string(AbsCachedArtifactPath); |
315 | } |
316 | |
317 | return createStringError(EC: errc::argument_out_of_domain, S: "build id not found" ); |
318 | } |
319 | |
320 | DebuginfodLogEntry::DebuginfodLogEntry(const Twine &Message) |
321 | : Message(Message.str()) {} |
322 | |
323 | void DebuginfodLog::push(const Twine &Message) { |
324 | push(Entry: DebuginfodLogEntry(Message)); |
325 | } |
326 | |
327 | void DebuginfodLog::push(DebuginfodLogEntry Entry) { |
328 | { |
329 | std::lock_guard<std::mutex> Guard(QueueMutex); |
330 | LogEntryQueue.push(x: Entry); |
331 | } |
332 | QueueCondition.notify_one(); |
333 | } |
334 | |
335 | DebuginfodLogEntry DebuginfodLog::pop() { |
336 | { |
337 | std::unique_lock<std::mutex> Guard(QueueMutex); |
338 | // Wait for messages to be pushed into the queue. |
339 | QueueCondition.wait(lock&: Guard, p: [&] { return !LogEntryQueue.empty(); }); |
340 | } |
341 | std::lock_guard<std::mutex> Guard(QueueMutex); |
342 | if (!LogEntryQueue.size()) |
343 | llvm_unreachable("Expected message in the queue." ); |
344 | |
345 | DebuginfodLogEntry Entry = LogEntryQueue.front(); |
346 | LogEntryQueue.pop(); |
347 | return Entry; |
348 | } |
349 | |
350 | DebuginfodCollection::DebuginfodCollection(ArrayRef<StringRef> PathsRef, |
351 | DebuginfodLog &Log, |
352 | ThreadPoolInterface &Pool, |
353 | double MinInterval) |
354 | : Log(Log), Pool(Pool), MinInterval(MinInterval) { |
355 | for (StringRef Path : PathsRef) |
356 | Paths.push_back(Elt: Path.str()); |
357 | } |
358 | |
359 | Error DebuginfodCollection::update() { |
360 | std::lock_guard<sys::Mutex> Guard(UpdateMutex); |
361 | if (UpdateTimer.isRunning()) |
362 | UpdateTimer.stopTimer(); |
363 | UpdateTimer.clear(); |
364 | for (const std::string &Path : Paths) { |
365 | Log.push(Message: "Updating binaries at path " + Path); |
366 | if (Error Err = findBinaries(Path)) |
367 | return Err; |
368 | } |
369 | Log.push(Message: "Updated collection" ); |
370 | UpdateTimer.startTimer(); |
371 | return Error::success(); |
372 | } |
373 | |
374 | Expected<bool> DebuginfodCollection::updateIfStale() { |
375 | if (!UpdateTimer.isRunning()) |
376 | return false; |
377 | UpdateTimer.stopTimer(); |
378 | double Time = UpdateTimer.getTotalTime().getWallTime(); |
379 | UpdateTimer.startTimer(); |
380 | if (Time < MinInterval) |
381 | return false; |
382 | if (Error Err = update()) |
383 | return std::move(Err); |
384 | return true; |
385 | } |
386 | |
387 | Error DebuginfodCollection::updateForever(std::chrono::milliseconds Interval) { |
388 | while (true) { |
389 | if (Error Err = update()) |
390 | return Err; |
391 | std::this_thread::sleep_for(rtime: Interval); |
392 | } |
393 | llvm_unreachable("updateForever loop should never end" ); |
394 | } |
395 | |
396 | static bool hasELFMagic(StringRef FilePath) { |
397 | file_magic Type; |
398 | std::error_code EC = identify_magic(path: FilePath, result&: Type); |
399 | if (EC) |
400 | return false; |
401 | switch (Type) { |
402 | case file_magic::elf: |
403 | case file_magic::elf_relocatable: |
404 | case file_magic::elf_executable: |
405 | case file_magic::elf_shared_object: |
406 | case file_magic::elf_core: |
407 | return true; |
408 | default: |
409 | return false; |
410 | } |
411 | } |
412 | |
413 | Error DebuginfodCollection::findBinaries(StringRef Path) { |
414 | std::error_code EC; |
415 | sys::fs::recursive_directory_iterator I(Twine(Path), EC), E; |
416 | std::mutex IteratorMutex; |
417 | ThreadPoolTaskGroup IteratorGroup(Pool); |
418 | for (unsigned WorkerIndex = 0; WorkerIndex < Pool.getMaxConcurrency(); |
419 | WorkerIndex++) { |
420 | IteratorGroup.async(F: [&, this]() -> void { |
421 | std::string FilePath; |
422 | while (true) { |
423 | { |
424 | // Check if iteration is over or there is an error during iteration |
425 | std::lock_guard<std::mutex> Guard(IteratorMutex); |
426 | if (I == E || EC) |
427 | return; |
428 | // Grab a file path from the directory iterator and advance the |
429 | // iterator. |
430 | FilePath = I->path(); |
431 | I.increment(ec&: EC); |
432 | } |
433 | |
434 | // Inspect the file at this path to determine if it is debuginfo. |
435 | if (!hasELFMagic(FilePath)) |
436 | continue; |
437 | |
438 | Expected<object::OwningBinary<object::Binary>> BinOrErr = |
439 | object::createBinary(Path: FilePath); |
440 | |
441 | if (!BinOrErr) { |
442 | consumeError(Err: BinOrErr.takeError()); |
443 | continue; |
444 | } |
445 | object::Binary *Bin = std::move(BinOrErr.get().getBinary()); |
446 | if (!Bin->isObject()) |
447 | continue; |
448 | |
449 | // TODO: Support non-ELF binaries |
450 | object::ELFObjectFileBase *Object = |
451 | dyn_cast<object::ELFObjectFileBase>(Val: Bin); |
452 | if (!Object) |
453 | continue; |
454 | |
455 | BuildIDRef ID = getBuildID(Obj: Object); |
456 | if (ID.empty()) |
457 | continue; |
458 | |
459 | std::string IDString = buildIDToString(ID); |
460 | if (Object->hasDebugInfo()) { |
461 | std::lock_guard<sys::RWMutex> DebugBinariesGuard(DebugBinariesMutex); |
462 | (void)DebugBinaries.try_emplace(Key: IDString, Args: std::move(FilePath)); |
463 | } else { |
464 | std::lock_guard<sys::RWMutex> BinariesGuard(BinariesMutex); |
465 | (void)Binaries.try_emplace(Key: IDString, Args: std::move(FilePath)); |
466 | } |
467 | } |
468 | }); |
469 | } |
470 | IteratorGroup.wait(); |
471 | std::unique_lock<std::mutex> Guard(IteratorMutex); |
472 | if (EC) |
473 | return errorCodeToError(EC); |
474 | return Error::success(); |
475 | } |
476 | |
477 | Expected<std::optional<std::string>> |
478 | DebuginfodCollection::getBinaryPath(BuildIDRef ID) { |
479 | Log.push(Message: "getting binary path of ID " + buildIDToString(ID)); |
480 | std::shared_lock<sys::RWMutex> Guard(BinariesMutex); |
481 | auto Loc = Binaries.find(Key: buildIDToString(ID)); |
482 | if (Loc != Binaries.end()) { |
483 | std::string Path = Loc->getValue(); |
484 | return Path; |
485 | } |
486 | return std::nullopt; |
487 | } |
488 | |
489 | Expected<std::optional<std::string>> |
490 | DebuginfodCollection::getDebugBinaryPath(BuildIDRef ID) { |
491 | Log.push(Message: "getting debug binary path of ID " + buildIDToString(ID)); |
492 | std::shared_lock<sys::RWMutex> Guard(DebugBinariesMutex); |
493 | auto Loc = DebugBinaries.find(Key: buildIDToString(ID)); |
494 | if (Loc != DebugBinaries.end()) { |
495 | std::string Path = Loc->getValue(); |
496 | return Path; |
497 | } |
498 | return std::nullopt; |
499 | } |
500 | |
501 | Expected<std::string> DebuginfodCollection::findBinaryPath(BuildIDRef ID) { |
502 | { |
503 | // Check collection; perform on-demand update if stale. |
504 | Expected<std::optional<std::string>> PathOrErr = getBinaryPath(ID); |
505 | if (!PathOrErr) |
506 | return PathOrErr.takeError(); |
507 | std::optional<std::string> Path = *PathOrErr; |
508 | if (!Path) { |
509 | Expected<bool> UpdatedOrErr = updateIfStale(); |
510 | if (!UpdatedOrErr) |
511 | return UpdatedOrErr.takeError(); |
512 | if (*UpdatedOrErr) { |
513 | // Try once more. |
514 | PathOrErr = getBinaryPath(ID); |
515 | if (!PathOrErr) |
516 | return PathOrErr.takeError(); |
517 | Path = *PathOrErr; |
518 | } |
519 | } |
520 | if (Path) |
521 | return *Path; |
522 | } |
523 | |
524 | // Try federation. |
525 | Expected<std::string> PathOrErr = getCachedOrDownloadExecutable(ID); |
526 | if (!PathOrErr) |
527 | consumeError(Err: PathOrErr.takeError()); |
528 | |
529 | // Fall back to debug binary. |
530 | return findDebugBinaryPath(ID); |
531 | } |
532 | |
533 | Expected<std::string> DebuginfodCollection::findDebugBinaryPath(BuildIDRef ID) { |
534 | // Check collection; perform on-demand update if stale. |
535 | Expected<std::optional<std::string>> PathOrErr = getDebugBinaryPath(ID); |
536 | if (!PathOrErr) |
537 | return PathOrErr.takeError(); |
538 | std::optional<std::string> Path = *PathOrErr; |
539 | if (!Path) { |
540 | Expected<bool> UpdatedOrErr = updateIfStale(); |
541 | if (!UpdatedOrErr) |
542 | return UpdatedOrErr.takeError(); |
543 | if (*UpdatedOrErr) { |
544 | // Try once more. |
545 | PathOrErr = getBinaryPath(ID); |
546 | if (!PathOrErr) |
547 | return PathOrErr.takeError(); |
548 | Path = *PathOrErr; |
549 | } |
550 | } |
551 | if (Path) |
552 | return *Path; |
553 | |
554 | // Try federation. |
555 | return getCachedOrDownloadDebuginfo(ID); |
556 | } |
557 | |
558 | DebuginfodServer::DebuginfodServer(DebuginfodLog &Log, |
559 | DebuginfodCollection &Collection) |
560 | : Log(Log), Collection(Collection) { |
561 | cantFail( |
562 | Err: Server.get(UrlPathPattern: R"(/buildid/(.*)/debuginfo)" , Handler: [&](HTTPServerRequest Request) { |
563 | Log.push(Message: "GET " + Request.UrlPath); |
564 | std::string IDString; |
565 | if (!tryGetFromHex(Input: Request.UrlPathMatches[0], Output&: IDString)) { |
566 | Request.setResponse( |
567 | {.Code: 404, .ContentType: "text/plain" , .Body: "Build ID is not a hex string\n" }); |
568 | return; |
569 | } |
570 | object::BuildID ID(IDString.begin(), IDString.end()); |
571 | Expected<std::string> PathOrErr = Collection.findDebugBinaryPath(ID); |
572 | if (Error Err = PathOrErr.takeError()) { |
573 | consumeError(Err: std::move(Err)); |
574 | Request.setResponse({.Code: 404, .ContentType: "text/plain" , .Body: "Build ID not found\n" }); |
575 | return; |
576 | } |
577 | streamFile(Request, FilePath: *PathOrErr); |
578 | })); |
579 | cantFail( |
580 | Err: Server.get(UrlPathPattern: R"(/buildid/(.*)/executable)" , Handler: [&](HTTPServerRequest Request) { |
581 | Log.push(Message: "GET " + Request.UrlPath); |
582 | std::string IDString; |
583 | if (!tryGetFromHex(Input: Request.UrlPathMatches[0], Output&: IDString)) { |
584 | Request.setResponse( |
585 | {.Code: 404, .ContentType: "text/plain" , .Body: "Build ID is not a hex string\n" }); |
586 | return; |
587 | } |
588 | object::BuildID ID(IDString.begin(), IDString.end()); |
589 | Expected<std::string> PathOrErr = Collection.findBinaryPath(ID); |
590 | if (Error Err = PathOrErr.takeError()) { |
591 | consumeError(Err: std::move(Err)); |
592 | Request.setResponse({.Code: 404, .ContentType: "text/plain" , .Body: "Build ID not found\n" }); |
593 | return; |
594 | } |
595 | streamFile(Request, FilePath: *PathOrErr); |
596 | })); |
597 | } |
598 | |
599 | } // namespace llvm |
600 | |