Files
llvm-project/lldb/unittests/Symbol/SymStoreTest.cpp
Stefan Gränitz 69c566bf7e [lldb] Add caching and _NT_SYMBOL_PATH parsing in SymbolLocatorSymStore (#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

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.

---------

Co-authored-by: Nerixyz <nero.9@hotmail.de>
2026-04-20 11:23:11 +02:00

114 lines
4.8 KiB
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 "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*"), returns());
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"));
// 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"));
// Fall back to default cache.
auto default_cache = SymbolLocatorSymStore::GetSystemDefaultCachePath();
EXPECT_EQ(check("cache*;srv*\\\\corp"), returns(default_cache));
EXPECT_EQ(check("srv**https://symbols.mozilla.org"), returns(default_cache));
// 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"));
}