SBValue summaries are a great way to show some condensed info about a variable but summaries are meant to be a relatively short (so the text will fits in a reasonable field in the GUI Local values widget), and human readable, which limits the algorithmic utility of the data they hold. It would be nice to have a more computation friendly API for providing metadata about a SBValue. That’s particularly useful in the interaction between lldb & GUI programs that want to drive lldb.
So I propose adding a parallel structure to the Summaries and SyntheticChild Providers, a “SBValue metadata provider”. It would be bound to SBValues using the same Type lookup mechanism that Summaries and Synthetic Children use.
Since we don’t want lldb to necessarily have to comprehend the data that’s output, we want the result to be self-descriptive. The current way to do something like that in lldb is to return an SBStructuredData which providers can fill out as a dictionary of keys & values.
The next observation is that in this case there might be more than one provider of metadata per type. For instance, you could imagine asking a heap variable for it’s “interesting malloc information” and getting back the history stack for it’s allocation, or maybe even the entire history of the memory it is currently living in. You might also have some type that you can do “sanity checks” on. If the variable fails the sanity check you want the GUI to flag this specially. So you might also want to ask the same variable for its “sanity-check” status, which might just be “bad” and the GUI would draw the value in red or might have “why bad” which the UI could show in a tool tip. And at any given time, you might want the data from one or the other provider.
So I propose the Metadata Providers should also have a “provider name” along with the standard Type matcher. Then the SBValue.GetMetaData call would take a provider, and only return the metadata for the providers that match both the provider name and the Type Matcher.
Note, the addition of providers means that we can’t piggyback this feature on the extant Summary Providers, it needs to be a parallel structure. After all, there might be two types that match the Type matcher. The Summary lookup will return the first matching summary provider, but if only the second Type match is the provider name requested, then we’d have to use the second one for the Metadata.
So then in the end, on the provider writer side, you would produce a Python class that had:
class Provider:
def GetMetaData(valobj: SBValue):
"""Returns an SBStructuredData with metadata in key value form"""
static def GetProviderName():
static def GetMetaDataSchematic():
"""Returns a SBStructuredData with keys & doc strings"""
The last two calls would make it possible for lldb and the UI to help users understand this data even if it didn’t know about a particular provider. When a provider like this is registered, lldb will get the provider name(s) so we can tell you the legal providers, and the last call would allow us to present definitions for the meanings of the fields.
And on the client side, you would call:
SBStructuredData SBValue::GetMetadata(char *provider_name);
//Returns the global list of providers registered.
static SBStringList SBValue::GetProviderNames();
// This on seems optional to me, but might be useful:
// Returns the list of providers that match the type of the SBValue.
SBStringList SBValue::GetProviderNames();
// Returns a dictionary with the keys as returned from GetMetadata but the value is a definition string for the key.
static SBStructuredData SBValue::GetProviderDescription(char *provider_name);
And on the command line we’d have:
(lldb) type metadata add --classname <PythonClass> <Type Matcher options copied from type summary>
I think it makes sense for the provider class to know its own provider name, but if we don’t want to do it that way, we can do:
(lldb) type metadata add --classname <PythonClass> --providername sanity-check <Type Matcher Options>
and then we’d poke that name into the provider when we registered it.
Then to help users know what is available here:
(lldb) type metadata providers list
sanity-check
malloc
(lldb) type metadata provider show sanity-check
Goodness - a bool if True the variable passed the sanity-check
Checker - Tells which of the sanity-checks this variable failed
Reason - The detailed string from the failed sanity checker
(lldb) type metadata info --metadata-provider sanity-check <Value Specification>
sanity-check provider is MyPythonModule.MySanityCheckProvider
this last one is mostly for us, but the equivalent type summary info and type synthetic info have proved very handy to have.
and to see the actual metadata:
(lldb) frame variable -metadata-provider sanity-check
(int) Foo = 10
Metadata:
Goodness: False
Checker - The one true variable inspector
Reason: You didn't do it right
and the equivalent for target variable
and expr
.
We should also consider installing a set of standard do-nothing providers for common operations, like allocation history, sanity-check, related-source-location. That would provide more predictability in these common cases, making it easier to use. We could even check, when a new provider for one of the build-in provider types is registered, that it has the required keys for that provider type.