Index: llvm/include/llvm/Debuginfod/Debuginfod.h =================================================================== --- /dev/null +++ llvm/include/llvm/Debuginfod/Debuginfod.h @@ -0,0 +1,66 @@ +//===-- 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 { + +// Returns a binary BuildID as a normalized hex string. +std::string buildIDToString(const ArrayRef 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(StringRef BuildID, + StringRef SourceFilePath); + +// Fetches an executable by searching the default local cache directory and +// server URLs. +Expected getCachedOrDownloadExecutable(StringRef BuildID); + +// Fetches a debug binary by searching the default local cache directory and +// server URLs. +Expected getCachedOrDownloadDebuginfo(StringRef BuildID); + +// Fetches any debuginfod artifact using the default local cache directory and +// server URLs. +Expected getCachedOrDownloadArtifact(StringRef UniqueKey, + StringRef UrlPath); + +// 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,184 @@ +//===-- 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 asset 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 { +// Uses lowercase for compatibility with common debuginfod servers. +std::string buildIDToString(const ArrayRef BuildID) { + return llvm::toHex(BuildID, /*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. + +// Source fetching functions. +Expected getCachedOrDownloadSource(const ArrayRef BuildID, + StringRef SourceFilePath) { + return getCachedOrDownloadSource(llvm::toHex(BuildID, /*LowerCase=*/true), + SourceFilePath); +} + +Expected getCachedOrDownloadSource(StringRef BuildID, + StringRef SourceFilePath) { + SmallString<64> UrlPath; + // TODO: convert \ to / in SourceFilePath for compatibility with non-posix + // filesystems. + sys::path::append(UrlPath, sys::path::Style::posix, "buildid", BuildID, + "source", SourceFilePath); + return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath); +} + +// Executable fetching functions. +Expected +getCachedOrDownloadExecutable(const ArrayRef BuildID) { + return getCachedOrDownloadExecutable( + llvm::toHex(BuildID, /*LowerCase=*/true)); +} + +Expected getCachedOrDownloadExecutable(StringRef BuildID) { + SmallString<64> UrlPath; + sys::path::append(UrlPath, sys::path::Style::posix, "buildid", + BuildID.lower(), "executable"); + return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath); +} + +// Debuginfo fetching functions. +Expected +getCachedOrDownloadDebuginfo(const ArrayRef BuildID) { + return getCachedOrDownloadDebuginfo(llvm::toHex(BuildID, /*LowerCase=*/true)); +} + +Expected getCachedOrDownloadDebuginfo(StringRef BuildID) { + SmallString<64> UrlPath; + sys::path::append(UrlPath, sys::path::Style::posix, "buildid", + BuildID.lower(), "debuginfo"); + return getCachedOrDownloadArtifact(uniqueKey(UrlPath), UrlPath); +} + +// General fetching function. +Expected getCachedOrDownloadArtifact(StringRef UniqueKey, + StringRef UrlPath) { + Expected CacheDirOrErr = getDefaultDebuginfodCacheDirectory(); + if (Error Err = CacheDirOrErr.takeError()) + return Err; + std::string &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> AbsCachedAssetPath; + sys::path::append(AbsCachedAssetPath, 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(AbsCachedAssetPath); + + // The asset 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."); + HTTPClient::initialize(); + + for (const StringRef &ServerUrl : DebuginfodUrls) { + SmallString<64> AssetUrl; + sys::path::append(AssetUrl, sys::path::Style::posix, ServerUrl, UrlPath); + + Expected ResponseOrErr = HTTPClient().get(AssetUrl); + if (Error Err = ResponseOrErr.takeError()) + return Err; + + HTTPResponseBuffer &Response = *ResponseOrErr; + if (Response.Code != 200) + continue; + + // We have retrieved the asset 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 asset on disk. + return std::string(AbsCachedAssetPath); + } + + 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.