Index: llvm/include/llvm/Debuginfod/Debuginfod.h =================================================================== --- /dev/null +++ llvm/include/llvm/Debuginfod/Debuginfod.h @@ -0,0 +1,71 @@ +//===-- llvm/Debuginfod/Debuginfod.h - Debuginfod client --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the declarations of getCachedOrDownloadArtifact and +/// several convenience functions for specific artifact types: +/// getCachedOrDownloadSource, getCachedOrDownloadExecutable, and +/// getCachedOrDownloadDebuginfo. This file also declares +/// getDefaultDebuginfodUrls and getDefaultDebuginfodCacheDirectory. +/// +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFOD_DEBUGINFOD_H +#define LLVM_DEBUGINFOD_DEBUGINFOD_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" + +namespace llvm { + +typedef ArrayRef BuildIDRef; + +typedef SmallVector BuildID; + +// Finds default array of Debuginfod server URLs by checking DEBUGINFOD_URLS +// environment variable. +Expected> getDefaultDebuginfodUrls(); + +// Finds a default local file caching directory for the debuginfod client. +Expected getDefaultDebuginfodCacheDirectory(); + +// Fetches a specified source file by searching the default local cache +// directory and server URLs. +Expected getCachedOrDownloadSource(BuildIDRef ID, + StringRef SourceFilePath, + StringRef CacheDir = ""); + +// Fetches an executable by searching the default local cache directory and +// server URLs. +Expected getCachedOrDownloadExecutable(BuildIDRef ID, + StringRef CacheDir = ""); + +// Fetches a debug binary by searching the default local cache directory and +// server URLs. +Expected getCachedOrDownloadDebuginfo(BuildIDRef ID, + StringRef CacheDir = ""); + +// Fetches any debuginfod artifact using the default local cache directory and +// server URLs. +Expected getCachedOrDownloadArtifact(StringRef UniqueKey, + StringRef UrlPath, + StringRef CacheDir = ""); + +// Fetches any debuginfod artifact using the specified local cache directory and +// server URLs. If the file is found, uses the UniqueKey for the local cache +// file. +Expected +getCachedOrDownloadArtifact(StringRef UniqueKey, StringRef UrlPath, + StringRef CacheDirectoryPath, + ArrayRef DebuginfodUrls); + +} // end namespace llvm + +#endif Index: llvm/lib/CMakeLists.txt =================================================================== --- llvm/lib/CMakeLists.txt +++ llvm/lib/CMakeLists.txt @@ -25,6 +25,7 @@ add_subdirectory(ObjectYAML) add_subdirectory(Option) add_subdirectory(Remarks) +add_subdirectory(Debuginfod) add_subdirectory(DebugInfo) add_subdirectory(DWP) add_subdirectory(ExecutionEngine) Index: llvm/lib/Debuginfod/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/lib/Debuginfod/CMakeLists.txt @@ -0,0 +1,9 @@ +add_llvm_component_library(LLVMDebuginfod + Debuginfod.cpp + + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/Debuginfod + + LINK_COMPONENTS + Support + ) Index: llvm/lib/Debuginfod/Debuginfod.cpp =================================================================== --- /dev/null +++ llvm/lib/Debuginfod/Debuginfod.cpp @@ -0,0 +1,172 @@ +//===-- llvm/Debuginfod/Debuginfod.cpp - Debuginfod client library --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// This file defines the fetchInfo function, which retrieves +/// any of the three supported artifact types: (executable, debuginfo, source +/// file) associated with a build-id from debuginfod servers. If a source file +/// is to be fetched, its absolute path must be specified in the Description +/// argument to fetchInfo. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/Debuginfod/Debuginfod.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/CachePruning.h" +#include "llvm/Support/Caching.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/HTTPClient.h" +#include "llvm/Support/xxhash.h" + +using namespace llvm; + +static std::string uniqueKey(llvm::StringRef S) { return utostr(xxHash64(S)); } + +namespace llvm { +// Returns a binary BuildID as a normalized hex string. +// Uses lowercase for compatibility with common debuginfod servers. +static std::string buildIDToString(BuildIDRef ID) { + return llvm::toHex(ID, /*LowerCase=*/true); +} + +Expected> getDefaultDebuginfodUrls() { + const char *DebuginfodUrlsEnv = std::getenv("DEBUGINFOD_URLS"); + if (DebuginfodUrlsEnv == NULL) + return createStringError(errc::invalid_argument, + "Missing DEBUGINFOD_URLS environment variable."); + + SmallVector DebuginfodUrls; + StringRef(DebuginfodUrlsEnv).split(DebuginfodUrls, " "); + return DebuginfodUrls; +} + +Expected getDefaultDebuginfodCacheDirectory() { + SmallString<64> CacheDirectory; + if (!sys::path::cache_directory(CacheDirectory)) + return createStringError( + errc::io_error, "Unable to determine appropriate cache directory."); + return std::string(CacheDirectory); +} + +/// The following functions fetch a debuginfod artifact to a file in a local +/// cache and return the cached file path. They first search the local cache, +/// followed by the debuginfod servers. + +Expected getCachedOrDownloadSource(BuildIDRef ID, + StringRef SourceFilePath, + StringRef CacheDir) { + SmallString<64> UrlPath; + // TODO: convert \ to / in SourceFilePath for compatibility with non-posix + // filesystems. + sys::path::append(UrlPath, sys::path::Style::posix, "buildid", + buildIDToString(ID), "source", SourceFilePath); + return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath, CacheDir); +} + +Expected getCachedOrDownloadExecutable(BuildIDRef ID, + StringRef CacheDir) { + SmallString<64> UrlPath; + sys::path::append(UrlPath, sys::path::Style::posix, "buildid", + buildIDToString(ID), "executable"); + return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath, CacheDir); +} + +Expected getCachedOrDownloadDebuginfo(BuildIDRef ID, + StringRef CacheDir) { + SmallString<64> UrlPath; + sys::path::append(UrlPath, sys::path::Style::posix, "buildid", + buildIDToString(ID), "debuginfo"); + return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath, CacheDir); +} + +// General fetching function. +Expected getCachedOrDownloadArtifact(StringRef UniqueKey, + StringRef UrlPath, + StringRef CacheDirRef) { + SmallString<10> CacheDir = CacheDirRef; + if (CacheDir == "") { + Expected CacheDirOrErr = getDefaultDebuginfodCacheDirectory(); + if (Error Err = CacheDirOrErr.takeError()) + return Err; + CacheDir = *CacheDirOrErr; + } + Expected> DebuginfodUrlsOrErr = + getDefaultDebuginfodUrls(); + if (Error Err = DebuginfodUrlsOrErr.takeError()) + return Err; + SmallVector &DebuginfodUrls = *DebuginfodUrlsOrErr; + return getCachedOrDownloadArtifact(UniqueKey, UrlPath, CacheDir, + DebuginfodUrls); +} + +Expected +getCachedOrDownloadArtifact(StringRef UniqueKey, StringRef UrlPath, + StringRef CacheDirectoryPath, + ArrayRef DebuginfodUrls) { + SmallString<64> AbsCachedArtifactPath; + sys::path::append(AbsCachedArtifactPath, CacheDirectoryPath, + "llvmcache-" + UniqueKey); + + Expected CacheOrErr = + localCache("Debuginfod-client", ".debuginfod-client", CacheDirectoryPath, + [](size_t Task, std::unique_ptr MB) {}); + if (Error Err = CacheOrErr.takeError()) + return Err; + + FileCache Cache = *CacheOrErr; + // We choose an arbitrary Task parameter as we do not make use of it. + unsigned Task = 0; + Expected CacheAddStreamOrErr = Cache(Task, UniqueKey); + if (Error Err = CacheAddStreamOrErr.takeError()) + return Err; + AddStreamFn &CacheAddStream = *CacheAddStreamOrErr; + if (!CacheAddStream) + return std::string(AbsCachedArtifactPath); + + // The artifact was not found in the local cache, query the debuginfod + // servers. + if (!HTTPClient::isAvailable()) + return createStringError(errc::io_error, + "No working HTTP client is available."); + + for (const StringRef &ServerUrl : DebuginfodUrls) { + SmallString<64> ArtifactUrl; + sys::path::append(ArtifactUrl, sys::path::Style::posix, ServerUrl, UrlPath); + + Expected ResponseOrErr = HTTPClient().get(ArtifactUrl); + if (Error Err = ResponseOrErr.takeError()) + return Err; + + HTTPResponseBuffer &Response = *ResponseOrErr; + if (Response.Code != 200) + continue; + + // We have retrieved the artifact from this server, and now add it to the + // file cache. + Expected> FileStreamOrErr = + CacheAddStream(Task); + if (Error Err = FileStreamOrErr.takeError()) + return Err; + std::unique_ptr &FileStream = *FileStreamOrErr; + if (!Response.Body) + return createStringError( + errc::io_error, "Unallocated MemoryBuffer in HTTPResponseBuffer."); + + *FileStream->OS << StringRef(Response.Body->getBufferStart(), + Response.Body->getBufferSize()); + + // Return the path to the artifact on disk. + return std::string(AbsCachedArtifactPath); + } + + return createStringError(errc::argument_out_of_domain, "build id not found"); +} + +} // namespace llvm Index: llvm/unittests/CMakeLists.txt =================================================================== --- llvm/unittests/CMakeLists.txt +++ llvm/unittests/CMakeLists.txt @@ -22,6 +22,7 @@ add_subdirectory(Bitstream) add_subdirectory(CodeGen) add_subdirectory(DebugInfo) +add_subdirectory(Debuginfod) add_subdirectory(Demangle) add_subdirectory(ExecutionEngine) add_subdirectory(FileCheck) Index: llvm/unittests/Debuginfod/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/unittests/Debuginfod/CMakeLists.txt @@ -0,0 +1,9 @@ +set(LLVM_LINK_COMPONENTS + Debuginfod + ) + +add_llvm_unittest(DebuginfodTests + DebuginfodTests.cpp + ) + +target_link_libraries(DebuginfodTests PRIVATE LLVMTestingSupport) Index: llvm/unittests/Debuginfod/DebuginfodTests.cpp =================================================================== --- /dev/null +++ llvm/unittests/Debuginfod/DebuginfodTests.cpp @@ -0,0 +1,14 @@ +//===-- llvm/unittest/Support/DebuginfodTests.cpp - unit tests --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Debuginfod/Debuginfod.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +/// TODO: test Debuginfod client functionality. This will be easier to test with +/// either a working HTTP server or a refactor of the Debuginfod library.