[Lldb-commits] [lldb] [lldb] Add caching and _NT_SYMBOL_PATH parsing in SymbolLocatorSymStore (PR #191782)
Stefan Gränitz via lldb-commits
lldb-commits at lists.llvm.org
Mon Apr 13 02:39:58 PDT 2026
https://github.com/weliveindetail created https://github.com/llvm/llvm-project/pull/191782
The `_NT_SYMBOL_PATH` environment variable is the idiomatic way to set a system-wide lookup order of symbol servers and a local cache for SymStore. It holds a semicolon-separated list of entries in the following notations:
* `srv*[<cache>*]<source>` sets a source and an optional explicit cache
* `cache*<cache>` sets an implicit cache for all subsequent entries
* all other entries are bare local directories
In WinDGB we can reset the symbol path to a default like this:
```
> !sym noisy
> .symfix
DBGHELP: Symbol Search Path: cache*C:\SymCache;SRV*https://msdl.microsoft.com/download/symbols
```
Since symbol paths are closely intertwined with the caching of symbol files, this patch proposes support in LLDB for both features at once. `ParseEnvSymbolPaths()` implements the parsing logic, which processes entries of the symbol path string from left to right to create a series of LookupEntry objects that each store a source and a cache location. The source of a LookupEntry can be a local directory or an HTTP server address. The cache is a local directory or empty. This representation unifies the implicit vs. explicit caching options from the SymStore protocol.
The lookup remains in `LocateSymStoreEntry()` which we now invoke for each LookupEntry. Here we distinguish sources between HTTP servers and local directories. The latter doesn't change. We just moved the logging to clearly express the new cases. For HTTP downloads we now check the cache first and add files to it after download. The download itself keeps targeting a temporary file to avoid corrupt cache entries in case of interruptions. After all, the SymStore protocol has no check-sums!
We define a default cache path that is used as a fallback, if the symbol path doesn't specify any. It can be overridden through the `plugin.symbol-locator.symstore.cache` property. This is analog to Debuginfod and very handy since we want to move files out from temp after download. The default cache path is what we use if there are no other options.
The symbol path notation in the SymStore protocol carries quite some legacy. The SymStoreTest unittest checks that we can parse all the obscure combinations of possible server and cache entries.
>From b6c01d870a5fdea8a78e074a4b5906b525547641 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Mon, 13 Apr 2026 10:53:55 +0200
Subject: [PATCH] [lldb] Add caching and _NT_SYMBOL_PATH parsing in
SymbolLocatorSymStore
---
.../SymStore/SymbolLocatorSymStore.cpp | 220 ++++++++++++++++--
.../SymStore/SymbolLocatorSymStore.h | 7 +
.../SymbolLocatorSymStoreProperties.td | 3 +
lldb/test/API/symstore/TestSymStore.py | 56 ++++-
lldb/unittests/Symbol/CMakeLists.txt | 2 +
lldb/unittests/Symbol/SymStoreTest.cpp | 108 +++++++++
6 files changed, 378 insertions(+), 18 deletions(-)
create mode 100644 lldb/unittests/Symbol/SymStoreTest.cpp
diff --git a/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.cpp b/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.cpp
index 41b15c6725a26..3d0e6243f4d6d 100644
--- a/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.cpp
+++ b/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.cpp
@@ -58,6 +58,25 @@ class PluginProperties : public Properties {
m_collection_sp->GetPropertyAtIndexAsArgs(ePropertySymStoreURLs, urls);
return urls;
}
+
+ std::string GetCachePath() const {
+ OptionValueString *s =
+ m_collection_sp->GetPropertyAtIndexAsOptionValueString(
+ ePropertyCachePath);
+ if (s && !s->GetCurrentValueAsRef().empty())
+ return s->GetCurrentValue();
+ // Fall back to the platform cache directory.
+ llvm::SmallString<128> cache_dir;
+ if (llvm::sys::path::cache_directory(cache_dir)) {
+ llvm::sys::path::append(cache_dir, "lldb", "symstore");
+ return cache_dir.str().str();
+ }
+ // Last resort: use a subdirectory of the system temp directory.
+ constexpr bool erase_on_reboot = false;
+ llvm::sys::path::system_temp_directory(erase_on_reboot, cache_dir);
+ llvm::sys::path::append(cache_dir, "lldb", "symstore");
+ return cache_dir.str().str();
+ }
};
} // namespace
@@ -103,6 +122,61 @@ SymbolLocator *SymbolLocatorSymStore::CreateInstance() {
namespace {
+SymbolLocatorSymStore::LookupEntry make_lookup_entry(llvm::StringRef source) {
+ SymbolLocatorSymStore::LookupEntry entry;
+ entry.source = source.str();
+ entry.cache = std::nullopt;
+ return entry;
+}
+
+SymbolLocatorSymStore::LookupEntry make_lookup_entry(llvm::StringRef source,
+ llvm::StringRef cache) {
+ SymbolLocatorSymStore::LookupEntry entry;
+ entry.source = source.str();
+ entry.cache = cache.str();
+ return entry;
+}
+
+std::vector<SymbolLocatorSymStore::LookupEntry> getGlobalLookupOrder() {
+ std::vector<SymbolLocatorSymStore::LookupEntry> result;
+
+ for (const auto &url : GetGlobalPluginProperties().GetURLs())
+ result.push_back(make_lookup_entry(url.ref()));
+
+ const char *sym_path = std::getenv("_NT_SYMBOL_PATH");
+ for (auto entry : SymbolLocatorSymStore::ParseEnvSymbolPaths(sym_path))
+ result.push_back(std::move(entry));
+
+ const char *alt_path = std::getenv("_NT_ALT_SYMBOL_PATH");
+ for (auto entry : SymbolLocatorSymStore::ParseEnvSymbolPaths(alt_path))
+ result.push_back(std::move(entry));
+
+ return result;
+}
+
+std::optional<SymbolLocatorSymStore::LookupEntry>
+ParseSrvEntry(llvm::StringRef entry) {
+ llvm::SmallVector<llvm::StringRef, 4> parts;
+ entry.trim().split(parts, '*');
+ switch (parts.size()) {
+ case 2:
+ return make_lookup_entry(parts[1]);
+ case 3:
+ return make_lookup_entry(parts[2], parts[1]);
+ default:
+ return {}; // Ignore entries with invalid number of parts.
+ }
+}
+
+std::optional<std::string> ParseCacheEntry(llvm::StringRef entry) {
+ llvm::SmallVector<llvm::StringRef, 2> parts;
+ entry.trim().split(parts, '*');
+ // Ignore entries with invalid number of parts.
+ if (parts.size() != 2)
+ return {};
+ return parts.back().str();
+}
+
// RSDS entries store identity as a 20-byte UUID composed of 16-byte GUID and
// 4-byte age:
// 12345678-1234-5678-9ABC-DEF012345678-00000001
@@ -133,8 +207,6 @@ bool HasUnsafeCharacters(llvm::StringRef s) {
return s == "." || s == "..";
}
-// TODO: This is a dumb initial implementation: It always downloads the file and
-// doesn't validate the result.
std::optional<FileSpec>
RequestFileFromSymStoreServerHTTP(llvm::StringRef base_url, llvm::StringRef key,
llvm::StringRef pdb_name) {
@@ -151,11 +223,9 @@ RequestFileFromSymStoreServerHTTP(llvm::StringRef base_url, llvm::StringRef key,
// Download into a temporary file. Cache coming soon.
llvm::SmallString<128> tmp_file;
- std::string tmp_file_name =
- llvm::formatv("lldb_symstore_{0}_{1}", key, pdb_name);
constexpr bool erase_on_reboot = true;
path::system_temp_directory(erase_on_reboot, tmp_file);
- path::append(tmp_file, tmp_file_name);
+ path::append(tmp_file, llvm::formatv("lldb_symstore_{0}_{1}", key, pdb_name));
// Server has SymStore directory structure with forward slashes as separators.
std::string source_url =
@@ -223,16 +293,86 @@ std::optional<FileSpec> FindFileInLocalSymStore(llvm::StringRef root_dir,
return spec;
}
-std::optional<FileSpec> LocateSymStoreEntry(llvm::StringRef base_url,
+std::optional<FileSpec> MoveToLocalSymStore(llvm::StringRef cache,
llvm::StringRef key,
- llvm::StringRef pdb_name) {
- if (base_url.starts_with("http://") || base_url.starts_with("https://"))
- return RequestFileFromSymStoreServerHTTP(base_url, key, pdb_name);
+ llvm::StringRef pdb_name,
+ FileSpec tmp_file) {
+ // Caches have SymStore directory structure: cache/pdb_name/key/pdb_name
+ llvm::SmallString<256> dest_dir;
+ llvm::sys::path::append(dest_dir, cache, pdb_name, key);
+ if (std::error_code ec = llvm::sys::fs::create_directories(dest_dir)) {
+ Debugger::ReportWarning(
+ llvm::formatv("failed to create SymStore cache directory '{0}': {1}",
+ dest_dir, ec.message()));
+ return tmp_file;
+ }
+
+ llvm::SmallString<256> dest;
+ llvm::sys::path::append(dest, dest_dir, pdb_name);
+ std::error_code ec = llvm::sys::fs::rename(tmp_file.GetPath(), dest);
+
+ // Fall back to copy+delete if we move to a different volume.
+ if (ec == std::errc::cross_device_link) {
+ ec = llvm::sys::fs::copy_file(tmp_file.GetPath(), dest);
+ if (!ec)
+ llvm::sys::fs::remove(tmp_file.GetPath());
+ }
+ if (ec) {
+ Debugger::ReportWarning(
+ llvm::formatv("failed to move '{0}' to SymStore cache '{1}': {2}",
+ tmp_file.GetPath(), dest, ec.message()));
+ return tmp_file;
+ }
+
+ return FileSpec(dest.str());
+}
+
+std::optional<FileSpec>
+LocateSymStoreEntry(const SymbolLocatorSymStore::LookupEntry &entry,
+ llvm::StringRef key, llvm::StringRef pdb_name) {
+ Log *log = GetLog(LLDBLog::Symbols);
+ std::string default_cache = GetGlobalPluginProperties().GetCachePath();
+
+ llvm::StringRef url = entry.source;
+ if (url.starts_with("http://") || url.starts_with("https://")) {
+ // Check cache first.
+ if (entry.cache) {
+ if (auto spec = FindFileInLocalSymStore(*entry.cache, key, pdb_name)) {
+ LLDB_LOG_VERBOSE(log, "Found {0} in SymStore cache {1}", pdb_name,
+ *entry.cache);
+ return *spec;
+ }
+ } else {
+ // Check LLDB default cache to avoid duplicate downloads in the same
+ // session.
+ if (auto spec = FindFileInLocalSymStore(default_cache, key, pdb_name)) {
+ LLDB_LOG_VERBOSE(log, "Found {0} in SymStore cache {1}", pdb_name,
+ default_cache);
+ return *spec;
+ }
+ }
+
+ // Download and move to cache.
+ if (auto spec = RequestFileFromSymStoreServerHTTP(url, key, pdb_name)) {
+ LLDB_LOG_VERBOSE(log, "Downloaded {0} from SymStore {1}", pdb_name, url);
+ std::string cache = entry.cache.value_or(default_cache);
+ spec = MoveToLocalSymStore(cache, key, pdb_name, *spec);
+ LLDB_LOG_VERBOSE(log, "Added {0} to SymStore cache {1}", pdb_name, cache);
+ return *spec;
+ }
- if (base_url.starts_with("file://"))
- base_url = base_url.drop_front(7);
+ return {};
+ }
- return FindFileInLocalSymStore(base_url, key, pdb_name);
+ llvm::StringRef file = entry.source;
+ if (file.starts_with("file://"))
+ file = file.drop_front(7);
+ if (auto spec = FindFileInLocalSymStore(file, key, pdb_name)) {
+ LLDB_LOG_VERBOSE(log, "Found {0} in local SymStore {1}", pdb_name, file);
+ return *spec;
+ }
+
+ return {};
}
} // namespace
@@ -261,13 +401,59 @@ std::optional<FileSpec> SymbolLocatorSymStore::LocateExecutableSymbolFile(
}
std::string key = FormatSymStoreKey(uuid);
- Args sym_store_urls = GetGlobalPluginProperties().GetURLs();
- for (const Args::ArgEntry &url : sym_store_urls) {
- if (auto spec = LocateSymStoreEntry(url.ref(), key, pdb_name)) {
- LLDB_LOG_VERBOSE(log, "Found {0} in SymStore {1}", pdb_name, url.ref());
+ for (const LookupEntry &entry : getGlobalLookupOrder()) {
+ if (auto spec = LocateSymStoreEntry(entry, key, pdb_name))
return *spec;
- }
}
return {};
}
+
+std::vector<SymbolLocatorSymStore::LookupEntry>
+SymbolLocatorSymStore::ParseEnvSymbolPaths(llvm::StringRef val) {
+ if (val.empty())
+ return {};
+
+ std::vector<LookupEntry> result;
+ std::optional<std::string> implicit_cache;
+ llvm::SmallVector<llvm::StringRef, 2> entries;
+ val.split(entries, ';');
+
+ for (llvm::StringRef raw : entries) {
+ llvm::StringRef entry = raw.trim();
+ if (entry.empty())
+ continue;
+
+ // Explicit cache directives apply to all subsequent srv* entries that don't
+ // set their own explicit cache.
+ if (entry.starts_with_insensitive("cache*")) {
+ if (auto cache = ParseCacheEntry(entry))
+ implicit_cache = *cache;
+ continue;
+ }
+
+ // SymStore directives with explicit interpreters are unsupported
+ // explicitly.
+ if (entry.starts_with_insensitive("symsrv*")) {
+ Debugger::ReportWarning(
+ llvm::formatv("ignoring unsupported entry in env: {0}", entry));
+ continue;
+ }
+
+ // SymStore server directives may include an explicit cache.
+ // Format is: srv*[LocalCache*]SymbolStore
+ if (entry.starts_with_insensitive("srv*")) {
+ if (auto lookup_entry = ParseSrvEntry(entry)) {
+ if (!lookup_entry->cache && implicit_cache)
+ lookup_entry->cache = implicit_cache;
+ result.push_back(*lookup_entry);
+ }
+ continue;
+ }
+
+ // Plain local paths aren't cached.
+ result.push_back(make_lookup_entry(entry));
+ }
+
+ return result;
+}
diff --git a/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.h b/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.h
index 52ec04cae387b..de2a1c6b9bdd5 100644
--- a/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.h
+++ b/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.h
@@ -43,6 +43,13 @@ class SymbolLocatorSymStore : public SymbolLocator {
static std::optional<FileSpec>
LocateExecutableSymbolFile(const ModuleSpec &module_spec,
const FileSpecList &default_search_paths);
+
+ struct LookupEntry {
+ std::string source;
+ std::optional<std::string> cache;
+ };
+
+ static std::vector<LookupEntry> ParseEnvSymbolPaths(llvm::StringRef val);
};
} // namespace lldb_private
diff --git a/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStoreProperties.td b/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStoreProperties.td
index 0cd631a80b90b..337f570b2ec7e 100644
--- a/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStoreProperties.td
+++ b/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStoreProperties.td
@@ -4,4 +4,7 @@ let Definition = "symbollocatorsymstore", Path = "plugin.symbol-locator.symstore
def SymStoreURLs : Property<"urls", "Array">,
ElementType<"String">,
Desc<"List of local symstore directories to query for symbols">;
+ def CachePath : Property<"cache", "String">,
+ DefaultStringValue<"">,
+ Desc<"Default cache directory for downloaded symbol files. Used when no cache is specified in _NT_SYMBOL_PATH.">;
}
diff --git a/lldb/test/API/symstore/TestSymStore.py b/lldb/test/API/symstore/TestSymStore.py
index 5baa5fa4d0da8..6af45dbe7e56c 100644
--- a/lldb/test/API/symstore/TestSymStore.py
+++ b/lldb/test/API/symstore/TestSymStore.py
@@ -66,6 +66,26 @@ def __exit__(self, *exc_info):
self._test.runCmd("settings clear plugin.symbol-locator.symstore")
+class NtSymbolPath:
+ """
+ Context Manager to temporarily set the _NT_SYMBOL_PATH environment variable.
+ """
+
+ def __init__(self, value):
+ self._value = value
+ self._saved = None
+
+ def __enter__(self):
+ self._saved = os.environ.get("_NT_SYMBOL_PATH")
+ os.environ["_NT_SYMBOL_PATH"] = self._value
+
+ def __exit__(self, *exc_info):
+ if self._saved is None:
+ os.environ.pop("_NT_SYMBOL_PATH", None)
+ else:
+ os.environ["_NT_SYMBOL_PATH"] = self._saved
+
+
class HTTPServer:
"""
Context Manager to serve a local directory tree via HTTP.
@@ -162,10 +182,44 @@ def test_http_not_found(self):
# certs, non-HTTPS redirects, etc.
def test_http(self):
"""
- Check that breakpoint hits with remote SymStore.
+ Check that breakpoint resolves with remote SymStore.
"""
exe, sym = self.build_inferior()
with MockedSymStore(self, exe, sym) as dir:
with HTTPServer(dir) as url:
self.runCmd(f"settings set plugin.symbol-locator.symstore.urls {url}")
self.try_breakpoint(exe, should_have_loc=True)
+
+ def test_nt_symbol_path_local(self):
+ """
+ Check that breakpoint resolves with a local SymStore path in
+ _NT_SYMBOL_PATH, and that the PDB is not copied to the cache.
+ """
+ exe, sym = self.build_inferior()
+ cache_dir = self.getBuildArtifact("cache")
+ symstore = MockedSymStore(self, exe, sym)
+ with symstore as dir:
+ self.runCmd(
+ f"settings set plugin.symbol-locator.symstore.cache {cache_dir}"
+ )
+ with NtSymbolPath(dir):
+ self.try_breakpoint(exe, should_have_loc=True)
+ self.assertFalse(any(files for _, _, files in os.walk(cache_dir)))
+
+ def test_nt_symbol_path_srv(self):
+ """
+ Check that breakpoint resolves with an HTTP symbol server in
+ _NT_SYMBOL_PATH using the srv* syntax, and that the PDB is cached.
+ """
+ exe, sym = self.build_inferior()
+ cache_dir = self.getBuildArtifact("cache")
+ symstore = MockedSymStore(self, exe, sym)
+ with symstore as dir:
+ self.runCmd(
+ f"settings set plugin.symbol-locator.symstore.cache {cache_dir}"
+ )
+ with HTTPServer(dir) as url:
+ with NtSymbolPath(f"srv*{url}"):
+ self.try_breakpoint(exe, should_have_loc=True)
+ key = symstore.get_key_pdb(exe)
+ self.assertTrue(os.path.isfile(os.path.join(cache_dir, sym, key, sym)))
diff --git a/lldb/unittests/Symbol/CMakeLists.txt b/lldb/unittests/Symbol/CMakeLists.txt
index 5664c21adbe3f..f9794369a89f4 100644
--- a/lldb/unittests/Symbol/CMakeLists.txt
+++ b/lldb/unittests/Symbol/CMakeLists.txt
@@ -6,6 +6,7 @@ add_lldb_unittest(SymbolTests
PostfixExpressionTest.cpp
SymbolTest.cpp
SymtabTest.cpp
+ SymStoreTest.cpp
TestTypeSystem.cpp
TestTypeSystemClang.cpp
TestClangASTImporter.cpp
@@ -24,6 +25,7 @@ add_lldb_unittest(SymbolTests
lldbPluginSymbolFileDWARF
lldbPluginSymbolFileSymtab
lldbPluginTypeSystemClang
+ lldbPluginSymbolLocatorSymStore
LLVMTestingSupport
)
diff --git a/lldb/unittests/Symbol/SymStoreTest.cpp b/lldb/unittests/Symbol/SymStoreTest.cpp
new file mode 100644
index 0000000000000..b08cdadf74400
--- /dev/null
+++ b/lldb/unittests/Symbol/SymStoreTest.cpp
@@ -0,0 +1,108 @@
+//===-- SymbolsTest.cpp ---------------------------------------------------===//
+//
+// 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 "gtest/gtest.h"
+
+#include "Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.h"
+
+using namespace lldb_private;
+using LookupEntry = SymbolLocatorSymStore::LookupEntry;
+
+TEST(SymStoreTest, ParseEnvSymbolPaths_Srv) {
+ auto check = [](const char *str) {
+ std::vector<std::string> sources;
+ for (LookupEntry entry : SymbolLocatorSymStore::ParseEnvSymbolPaths(str))
+ sources.push_back(std::move(entry.source));
+ return sources;
+ };
+ auto returns = [](auto... strs) { return std::vector<std::string>{strs...}; };
+
+ // Local paths
+ EXPECT_EQ(check(""), returns());
+ EXPECT_EQ(check("C:\\ProgramData\\Symbols"),
+ returns("C:\\ProgramData\\Symbols"));
+ EXPECT_EQ(check("C:\\symbols;\\\\buildserver\\syms;file://D:/pdb"),
+ returns("C:\\symbols", "\\\\buildserver\\syms", "file://D:/pdb"));
+
+ // Symbol servers
+ EXPECT_EQ(check("srv*https://msdl.microsoft.com/download/symbols"),
+ returns("https://msdl.microsoft.com/download/symbols"));
+ EXPECT_EQ(check("Srv*https://msdl.microsoft.com/download/symbols"),
+ returns("https://msdl.microsoft.com/download/symbols"));
+ EXPECT_EQ(check("SRV*http://localhost"), returns("http://localhost"));
+
+ // Symbol servers and local paths with caches
+ EXPECT_EQ(check("SRV*C:\\symcache*\\\\corp\\symbols"),
+ returns("\\\\corp\\symbols"));
+ EXPECT_EQ(check("D:\\sym;srv*C:\\symcache*D:\\sym"),
+ returns("D:\\sym", "D:\\sym"));
+ EXPECT_EQ(check("srv**https://symbols.mozilla.org"),
+ returns("https://symbols.mozilla.org"));
+
+ // Symbol server with custom implementation (unsupported)
+ EXPECT_EQ(check("symsrv*symsrv.dll*https://symbols.mozilla.org"), returns());
+ EXPECT_EQ(check("symsrv*symsrv.dll*C:\\symbols*https://symbols.mozilla.org"),
+ returns());
+ EXPECT_EQ(check("symsrv*https://symbols.mozilla.org;D:\\sym"),
+ returns("D:\\sym"));
+
+ // Partially invalid specs
+ EXPECT_EQ(check("srv*;;D:\\sym;SRV*"), returns("", "D:\\sym", ""));
+ EXPECT_EQ(check("srv*D:\\1*D:\\2*D:\\3;D:\\sym"), returns("D:\\sym"));
+ EXPECT_EQ(check("symsrv*D:\\1;D:\\sym"), returns("D:\\sym"));
+}
+
+TEST(SymStoreTest, ParseEnvSymbolPaths_Cache) {
+ auto check = [](const char *str) {
+ std::vector<std::string> caches;
+ for (LookupEntry entry : SymbolLocatorSymStore::ParseEnvSymbolPaths(str))
+ if (entry.cache)
+ caches.push_back(std::move(*entry.cache));
+ return caches;
+ };
+ auto returns = [](auto... strs) { return std::vector<std::string>{strs...}; };
+
+ // No caches
+ EXPECT_EQ(check(""), returns());
+ EXPECT_EQ(check("C:\\ProgramData\\Symbols"), returns());
+ EXPECT_EQ(check("C:\\symbols;\\\\buildserver\\syms;file://D:/pdb"),
+ returns());
+ EXPECT_EQ(check("SRV*http://localhost"), returns());
+
+ // No cache without a server
+ EXPECT_EQ(check("cache*C:\\symcache"), returns());
+ EXPECT_EQ(check("cache*C:\\symcache;D:\\sym"), returns());
+
+ // Explicit caches for symbol servers
+ EXPECT_EQ(check("SRV*C:\\symcache*\\\\corp\\symbols"),
+ returns("C:\\symcache"));
+ EXPECT_EQ(check("D:\\sym;srv*C:\\symcache*D:\\sym"), returns("C:\\symcache"));
+ EXPECT_EQ(check("srv**https://symbols.mozilla.org"), returns(""));
+
+ // Implicit caches for following symbol servers
+ EXPECT_EQ(check("cache*D:\\s;srv*\\\\corp"), returns("D:\\s"));
+ EXPECT_EQ(check("CACHE*D:\\s;srv*\\\\corp;SRV*http://localhost"),
+ returns("D:\\s", "D:\\s"));
+ EXPECT_EQ(check("Cache*D:\\s;srv*\\\\corp;SRV*C:\\X*http://localhost"),
+ returns("D:\\s", "C:\\X"));
+ EXPECT_EQ(check("srv*\\\\corp;cache*D:\\s;SRV*C:\\X*http://localhost"),
+ returns("C:\\X"));
+ EXPECT_EQ(check("srv*\\\\corp;SRV*C:\\X*http://localhost;cache*D:\\s"),
+ returns("C:\\X"));
+
+ // Symbol server with custom implementation (unsupported)
+ EXPECT_EQ(check("symsrv*symsrv.dll*https://symbols.mozilla.org"), returns());
+ EXPECT_EQ(check("symsrv*symsrv.dll*C:\\symbols*https://symbols.mozilla.org"),
+ returns());
+
+ // Partially invalid specs
+ EXPECT_EQ(check("cache*C:\\1;;D:\\sym;SRV*"), returns("C:\\1"));
+ EXPECT_EQ(check("cache*C:\\1;srv*D:\\1*D:\\2*D:\\3;srv*D:\\sym"),
+ returns("C:\\1"));
+ EXPECT_EQ(check("cache*C:\\1;symsrv*D:\\1;srv*D:\\sym"), returns("C:\\1"));
+}
More information about the lldb-commits
mailing list