LLDB mainline
SymbolLocatorSymStore.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
10
15#include "lldb/Utility/Args.h"
17#include "lldb/Utility/Log.h"
18#include "lldb/Utility/UUID.h"
19
20#include "llvm/ADT/StringExtras.h"
21#include "llvm/Support/Endian.h"
22#include "llvm/Support/FileSystem.h"
23#include "llvm/Support/FormatVariadic.h"
24#include "llvm/Support/HTTP/HTTPClient.h"
25#include "llvm/Support/Path.h"
26
27using namespace lldb;
28using namespace lldb_private;
29
31
32namespace {
33
34#define LLDB_PROPERTIES_symbollocatorsymstore
35#include "SymbolLocatorSymStoreProperties.inc"
36
37enum {
38#define LLDB_PROPERTIES_symbollocatorsymstore
39#include "SymbolLocatorSymStorePropertiesEnum.inc"
40};
41
42class PluginProperties : public Properties {
43public:
44 static llvm::StringRef GetSettingName() {
46 }
47
48 PluginProperties() {
49 m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
50 m_collection_sp->Initialize(g_symbollocatorsymstore_properties_def);
51 }
52
53 Args GetURLs() const {
54 Args urls;
55 m_collection_sp->GetPropertyAtIndexAsArgs(ePropertySymStoreURLs, urls);
56 return urls;
57 }
58};
59
60} // namespace
61
62static PluginProperties &GetGlobalPluginProperties() {
63 static PluginProperties g_settings;
64 return g_settings;
65}
66
68
76
79 debugger, PluginProperties::GetSettingName())) {
80 constexpr bool is_global_setting = true;
82 debugger, GetGlobalPluginProperties().GetValueProperties(),
83 "Properties for the SymStore Symbol Locator plug-in.",
84 is_global_setting);
85 }
86}
87
92
94 return "Symbol locator for PDB in SymStore";
95}
96
100
101namespace {
102
103// RSDS entries store identity as a 20-byte UUID composed of 16-byte GUID and
104// 4-byte age:
105// 12345678-1234-5678-9ABC-DEF012345678-00000001
106//
107// SymStore key is a string with no separators and age as decimal:
108// 12345678123456789ABCDEF0123456781
109//
110std::string formatSymStoreKey(const UUID &uuid) {
111 llvm::ArrayRef<uint8_t> bytes = uuid.GetBytes();
112 uint32_t age = llvm::support::endian::read32be(bytes.data() + 16);
113 constexpr bool LowerCase = false;
114 return llvm::toHex(bytes.slice(0, 16), LowerCase) + std::to_string(age);
115}
116
117// This is a simple version of Debuginfod's StreamedHTTPResponseHandler. We
118// should consider reusing that once we introduce caching.
119class FileDownloadHandler : public llvm::HTTPResponseHandler {
120private:
121 std::error_code m_ec;
122 llvm::raw_fd_ostream m_stream;
123
124public:
125 FileDownloadHandler(llvm::StringRef file) : m_stream(file.str(), m_ec) {}
126 virtual ~FileDownloadHandler() = default;
127
128 llvm::Error handleBodyChunk(llvm::StringRef data) override {
129 // Propagate error from ctor.
130 if (m_ec)
131 return llvm::createStringError(m_ec, "Failed to open file for writing");
132 m_stream.write(data.data(), data.size());
133 if (std::error_code ec = m_stream.error())
134 return llvm::createStringError(ec, "Error writing to file");
135
136 return llvm::Error::success();
137 }
138};
139
140llvm::Error downloadFileHTTP(llvm::StringRef url, FileDownloadHandler dest) {
141 if (!llvm::HTTPClient::isAvailable())
142 return llvm::createStringError(
143 std::make_error_code(std::errc::not_supported),
144 "HTTP client is not available");
145 llvm::HTTPRequest Request(url);
146 Request.FollowRedirects = true;
147
148 llvm::HTTPClient Client;
149
150 // TODO: Since PDBs can be huge, we should distinguish between resolve,
151 // connect, send and receive.
152 Client.setTimeout(std::chrono::seconds(60));
153
154 if (llvm::Error Err = Client.perform(Request, dest))
155 return Err;
156
157 unsigned ResponseCode = Client.responseCode();
158 if (ResponseCode != 200) {
159 return llvm::createStringError(std::make_error_code(std::errc::io_error),
160 "HTTP request failed with status code " +
161 std::to_string(ResponseCode));
162 }
163
164 return llvm::Error::success();
165}
166
167bool has_unsafe_characters(llvm::StringRef s) {
168 for (unsigned char c : s) {
169 // RFC 3986 unreserved characters are safe for file names and URLs.
170 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
171 (c >= '0' && c <= '9') || c == '-' || c == '.' || c == '_' ||
172 c == '~') {
173 continue;
174 }
175
176 return true;
177 }
178
179 // Avoid path semantics issues.
180 return s == "." || s == "..";
181}
182
183// TODO: This is a dump initial implementation: It always downloads the file, it
184// doesn't validate the result, it doesn't employ proper buffering for large
185// files.
186std::optional<FileSpec>
187requestFileFromSymStoreServerHTTP(llvm::StringRef base_url, llvm::StringRef key,
188 llvm::StringRef pdb_name) {
189 using namespace llvm::sys;
191
192 // Make sure URL will be valid, portable, and compatible with symbol servers.
193 if (has_unsafe_characters(pdb_name)) {
194 Debugger::ReportWarning(llvm::formatv(
195 "rejecting HTTP lookup for PDB file due to unsafe characters in "
196 "name: {0}",
197 pdb_name));
198 return {};
199 }
200
201 // Download into a temporary file. Cache coming soon.
202 llvm::SmallString<128> tmp_file;
203 std::string tmp_file_name =
204 llvm::formatv("lldb_symstore_{0}_{1}", key, pdb_name);
205 constexpr bool erase_on_reboot = true;
206 path::system_temp_directory(erase_on_reboot, tmp_file);
207 path::append(tmp_file, tmp_file_name);
208
209 // Server has SymStore directory structure with forward slashes as separators.
210 std::string source_url =
211 llvm::formatv("{0}/{1}/{2}/{1}", base_url, pdb_name, key);
212 if (llvm::Error err = downloadFileHTTP(source_url, tmp_file.str())) {
213 LLDB_LOG_ERROR(log, std::move(err),
214 "Failed to download from SymStore '{1}': {0}", source_url);
215 return {};
216 }
217
218 return FileSpec(tmp_file.str());
219}
220
221std::optional<FileSpec> findFileInLocalSymStore(llvm::StringRef root_dir,
222 llvm::StringRef key,
223 llvm::StringRef pdb_name) {
224 llvm::SmallString<256> path;
225 llvm::sys::path::append(path, root_dir, pdb_name, key, pdb_name);
226 FileSpec spec(path);
227 if (!FileSystem::Instance().Exists(spec))
228 return {};
229
230 return spec;
231}
232
233std::optional<FileSpec> locateSymStoreEntry(llvm::StringRef base_url,
234 llvm::StringRef key,
235 llvm::StringRef pdb_name) {
236 if (base_url.starts_with("http://") || base_url.starts_with("https://"))
237 return requestFileFromSymStoreServerHTTP(base_url, key, pdb_name);
238
239 if (base_url.starts_with("file://"))
240 base_url = base_url.drop_front(7);
241
242 return findFileInLocalSymStore(base_url, key, pdb_name);
243}
244
245} // namespace
246
248 const ModuleSpec &module_spec, const FileSpecList &default_search_paths) {
249 const UUID &uuid = module_spec.GetUUID();
250 if (!uuid.IsValid() ||
251 !ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup())
252 return {};
253
255 std::string pdb_name =
256 module_spec.GetSymbolFileSpec().GetFilename().GetStringRef().str();
257 if (pdb_name.empty()) {
259 "Failed to resolve symbol PDB module: PDB name empty");
260 return {};
261 }
262
263 LLDB_LOG_VERBOSE(log, "LocateExecutableSymbolFile {0} with UUID {1}",
264 pdb_name, uuid.GetAsString());
265 if (uuid.GetBytes().size() != 20) {
266 LLDB_LOG_VERBOSE(log, "Failed to resolve symbol PDB module: UUID invalid");
267 return {};
268 }
269
270 std::string key = formatSymStoreKey(uuid);
271 Args sym_store_urls = GetGlobalPluginProperties().GetURLs();
272 for (const Args::ArgEntry &url : sym_store_urls) {
273 if (auto spec = locateSymStoreEntry(url.ref(), key, pdb_name)) {
274 LLDB_LOG_VERBOSE(log, "Found {0} in SymStore {1}", pdb_name, url.ref());
275 return *spec;
276 }
277 }
278
279 return {};
280}
static PluginProperties & GetGlobalPluginProperties()
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:394
#define LLDB_LOG_VERBOSE(log,...)
Definition Log.h:371
#define LLDB_PLUGIN_DEFINE(PluginName)
static PluginProperties & GetGlobalPluginProperties()
A command line argument class.
Definition Args.h:33
llvm::StringRef GetStringRef() const
Get the string value as a llvm::StringRef.
A class to manage flag bits.
Definition Debugger.h:101
static void ReportWarning(std::string message, std::optional< lldb::user_id_t > debugger_id=std::nullopt, std::once_flag *once=nullptr)
Report warning events.
A file collection class.
A file utility class.
Definition FileSpec.h:57
const ConstString & GetFilename() const
Filename string const get accessor.
Definition FileSpec.h:250
static FileSystem & Instance()
static ModuleListProperties & GetGlobalModuleListProperties()
FileSpec & GetSymbolFileSpec()
Definition ModuleSpec.h:81
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, ABICreateInstance create_callback)
static lldb::OptionValuePropertiesSP GetSettingForSymbolLocatorPlugin(Debugger &debugger, llvm::StringRef setting_name)
static bool UnregisterPlugin(ABICreateInstance create_callback)
static bool CreateSettingForSymbolLocatorPlugin(Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp, llvm::StringRef description, bool is_global_property)
This plugin implements lookup in Microsoft SymStore instances.
static void DebuggerInitialize(Debugger &debugger)
static lldb_private::SymbolLocator * CreateInstance()
static llvm::StringRef GetPluginNameStatic()
static llvm::StringRef GetPluginDescriptionStatic()
static std::optional< FileSpec > LocateExecutableSymbolFile(const ModuleSpec &module_spec, const FileSpecList &default_search_paths)
Represents UUID's of various sizes.
Definition UUID.h:27
llvm::ArrayRef< uint8_t > GetBytes() const
Definition UUID.h:66
std::string GetAsString(llvm::StringRef separator="-") const
Definition UUID.cpp:54
bool IsValid() const
Definition UUID.h:69
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:327