Skip to content

[lldb] add plugin names to process save-core error output. #143126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 23, 2025

Conversation

da-viper
Copy link
Contributor

@da-viper da-viper commented Jun 6, 2025

continuation of #142684 to show plugin names.

From issue #14258

@da-viper da-viper requested review from DavidSpickett and Jlalond June 6, 2025 12:29
@da-viper da-viper requested a review from JDevlieghere as a code owner June 6, 2025 12:29
@llvmbot llvmbot added the lldb label Jun 6, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 6, 2025

@llvm/pr-subscribers-lldb

Author: Ebuka Ezike (da-viper)

Changes

continuation of #142684 to show plugin names.

From issue #14258


Full diff: https://github.com/llvm/llvm-project/pull/143126.diff

3 Files Affected:

  • (modified) lldb/include/lldb/Core/PluginManager.h (+2)
  • (modified) lldb/source/Core/PluginManager.cpp (+10)
  • (modified) lldb/source/Symbol/SaveCoreOptions.cpp (+20-2)
diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h
index e2f709ecd2ff7..7996dc3fdf3f3 100644
--- a/lldb/include/lldb/Core/PluginManager.h
+++ b/lldb/include/lldb/Core/PluginManager.h
@@ -212,6 +212,8 @@ class PluginManager {
   static Status SaveCore(const lldb::ProcessSP &process_sp,
                          lldb_private::SaveCoreOptions &core_options);
 
+  static llvm::SmallVector<llvm::StringRef> GetSaveCorePluginNames();
+
   // ObjectContainer
   static bool RegisterPlugin(
       llvm::StringRef name, llvm::StringRef description,
diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp
index de815e6308838..d02e3030081dc 100644
--- a/lldb/source/Core/PluginManager.cpp
+++ b/lldb/source/Core/PluginManager.cpp
@@ -800,6 +800,16 @@ Status PluginManager::SaveCore(const lldb::ProcessSP &process_sp,
   return error;
 }
 
+llvm::SmallVector<llvm::StringRef> PluginManager::GetSaveCorePluginNames() {
+  llvm::SmallVector<llvm::StringRef> plugin_names;
+  auto instances = GetObjectFileInstances().GetSnapshot();
+  for (auto &instance : instances) {
+    if (instance.save_core)
+      plugin_names.emplace_back(instance.name);
+  }
+  return plugin_names;
+}
+
 #pragma mark ObjectContainer
 
 struct ObjectContainerInstance
diff --git a/lldb/source/Symbol/SaveCoreOptions.cpp b/lldb/source/Symbol/SaveCoreOptions.cpp
index d884b00a47b00..c1c85571c8a78 100644
--- a/lldb/source/Symbol/SaveCoreOptions.cpp
+++ b/lldb/source/Symbol/SaveCoreOptions.cpp
@@ -22,8 +22,26 @@ Status SaveCoreOptions::SetPluginName(const char *name) {
   }
 
   if (!PluginManager::IsRegisteredObjectFilePluginName(name)) {
-    return Status::FromErrorStringWithFormat(
-        "plugin name '%s' is not a valid ObjectFile plugin name", name);
+    StreamString stream;
+    stream.Printf("plugin name '%s' is not a valid ObjectFile plugin name.",
+                  name);
+
+    llvm::SmallVector<llvm::StringRef> plugin_names =
+        PluginManager::GetSaveCorePluginNames();
+    if (!plugin_names.empty()) {
+      stream.PutCString(" valid values are: ");
+      bool is_first = true;
+      for (llvm::StringRef plugin_name : plugin_names) {
+        llvm::StringRef delimiter = ", ";
+        if (is_first) {
+          delimiter = "";
+          is_first = false;
+        }
+        stream.Printf("%s\"%s\"", delimiter.data(), plugin_name.data());
+      }
+      stream.PutChar('.');
+    }
+    return Status(stream.GetString().str());
   }
 
   m_plugin_name = name;

@da-viper
Copy link
Contributor Author

da-viper commented Jun 6, 2025

Requested from the comments, I added the available plugins that can save a core file.
But not to sure how to add it to help plugin save-core because help files are all static and this help is dynamically generated.

@DavidSpickett
Copy link
Collaborator

But not to sure how to add it to help plugin save-core because help files are all static and this help is dynamically generated.

To repeat a comment from the issue, this is what ninja does for example:

  -d MODE  enable debugging (use '-d list' to list modes)
  -t TOOL  run a subtool (use '-t list' to list subtools)
    terminates toplevel options; further flags are passed to the tool
  -w FLAG  adjust warnings (use '-w list' to list warnings)
$ ninja check-flang-rt -d list
debugging modes:
  stats        print operation counts/timing info
  explain      explain what caused a command to execute
  keepdepfile  don't delete depfiles after they're read by ninja
  keeprsp      don't delete @response files on success
multiple modes can be enabled via -d FOO -d BAR

So their help text is static but -d list might show more or less things depending on what they add to ninja.

Now the cheapest way to do this is to say in the help: (use --plugin-name=list to list possible plugins)

And this will be an error because there's no "list" plugin, but you get the list of valid plugins so it's the same result for the user. Or you can special case "list" such that it's not an error, the only difference is the return status and the first line of the output.

Looking at this code, the point where you check the name is quite buried. If it were up in the process save-core command then it would be easier. I assume it's done this way because you can reach the code via the API as well, so I understand the decision.

You could do a special check earlier in the command but it's a lot of effort for the same user experience. I'm happy to overlook that it's technically an error to list the valid values.

So:

  • Add to the help text some static string like those above.
  • Use a name like list or help that's never going to be a valid plugin name.
  • No one will care that it's an error because now they know what name to use :)

@DavidSpickett
Copy link
Collaborator

We can generate help text at runtime (e.g.

CommandObjectTypeFormatterDelete(CommandInterpreter &interpreter,
) but I'm not sure you'd have access to the pluginmanager from there.

There's some stuff for tab completing plugin names, we could hook that up, but that feels like a nice to have. I'm not sure as a user I'd even think to try it anyway, something in the help/error message is much more discoverable.

@da-viper
Copy link
Contributor Author

da-viper commented Jun 9, 2025

I overrode the GetDefinitions to add the missing options for the plugin-name arg it seems like the generic way to do it.

Copy link
Collaborator

@DavidSpickett DavidSpickett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trying not to bog you down in nitpicks, honest :)

There are completion test cases (lldb/test/API/functionalities/completion/TestCompletion.py at least). See if you can add to those.

Copy link
Contributor

@Jlalond Jlalond left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented on my own real concern. David can approve the land, but please don't break the current default behavior :)

Copy link
Member

@JDevlieghere JDevlieghere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a test.

@da-viper
Copy link
Contributor Author

It seems that the completion for optional arguments is currently not working, I tested this with other commands such as

image list --arch , image list --triple and process load --install. The cause seems to be from after the parsing of the options for completion.

case OptionParser::eOptionalArgument:
option_element_vector.push_back(OptionArgElement(
opt_defs_index,
FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2],
args),
FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],
args)));

It first assumes that the, option argument will have a different index from the option. but it can only be

--plugin-name=minidump # valid optional argument
--plugin-name minidump # minidump is treated as a new argument in the commands entered.

# same for short option
-pminidump # valid
-p minidump # not valid

The other issue is that completions uses GetCursorArgumentPrefix from CompletionRequest, since that returns entire argument --plugin-name=m.. the current way it does will not match any item.

@da-viper
Copy link
Contributor Author

I did a little more digging, optional completion is not really supported anywhere.

GetCursorArgumentPrefix currently returns the entire --plugin-name=minidump,
There are two ways I am thinking of that can solve the problem.

  1. If completing optional args we match the option and return the option as a prefix every where.
    e.g.
command = "process save core --plugin-name=m"
parsed_args = ["process", "save-core", "--plugin-name=m"]
last_argument_prefix = "--plugin-name=m" 
# we have to add the option prefix to the completion. 
completion_returned = ["--plugin-name=minidump", "--plugin-name=mach-o"] 
  1. In parsing the command, separate the option from the argument.
command = "process save-core --plugin-name=m"
parsed_args = ["process", "save-core", "--plugin-name", "m"] # this may collide with the normal args. 
last_argument_prefix= "m"
competion_returned = ["minidump", "mach-o"] 

@DavidSpickett
Copy link
Collaborator

It seems that the completion for optional arguments is currently not working

There are two ways I am thinking of that can solve the problem.

How about you remove the completion parts from this PR and tackle that topic in another. That way you can get something landed sooner.

show the plugin names in when running `help plugin save-core`
@da-viper da-viper force-pushed the process-save-core-help branch from 14d4ff5 to fc849bd Compare June 15, 2025 09:50
@da-viper
Copy link
Contributor Author

da-viper commented Jun 15, 2025

How about you remove the completion parts from this PR and tackle that topic in another PR.

Dropped all completion related changes.

@DavidSpickett
Copy link
Collaborator

Once my comments are addressed this will be good to land. Only concern was changing the default behaviour, and this PR does not go anywhere near that now.

@da-viper
Copy link
Contributor Author

da-viper commented Jun 20, 2025

What's the full help look like now? I wonder where this "Values: " comes from. Just want to make sure it's not leftover from a previous version of this PR where we called the names "values" in the error message.

The Values come from the plugin help, this is what it looks like now.

(lldb) help process save-core
Save the current process as a core file using an appropriate file type.

Syntax: process save-core [-s corefile-style -p plugin-name] FILE

Command Options Usage:
  process save-core [-p[<plugin>]] [-s <corefile-style>] <path>

       -p[<plugin>] ( --plugin-name=[<plugin>] )
            Specify a plugin name to create the core file. This allows core files to be saved in different formats.
            Values: mach-o | minidump | pe-coff

       -s <corefile-style> ( --style <corefile-style> )
            Request a specific style of corefile to be saved.
            Values: full | modified-memory | stack
     
     This command takes options and free-form arguments.  If your arguments resemble option specifiers (i.e., they start with a - or --), you must use ' -- ' between the end of the
     command options and the beginning of the arguments.

@DavidSpickett
Copy link
Collaborator

Values: mach-o | minidump | pe-coff

I'm not sure anything other than minidump actually works, but technically that's not this patch's problem.

Is it possible these plugins don't override some default member function that needs to return false? Or they can save cores but I'd be surprised if so.

@DavidSpickett
Copy link
Collaborator

The Values come from the plugin help, this is what it looks like now.

This is fine then.

@DavidSpickett
Copy link
Collaborator

Just add the new tests and this will be good to go. Figure out whether the other plugins are lying later :)

@da-viper
Copy link
Contributor Author

The mach-o plugin works but the pecoff is an alias to minidump

bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp,
lldb_private::SaveCoreOptions &options,
Status &error) {
// The FileSpec and Process are already checked in PluginManager::SaveCore.
assert(options.GetOutputFile().has_value());
assert(process_sp);
const FileSpec outfile = options.GetOutputFile().value();
// MachO defaults to dirty pages
if (options.GetStyle() == SaveCoreStyle::eSaveCoreUnspecified)
options.SetStyle(eSaveCoreDirtyOnly);
Target &target = process_sp->GetTarget();
const ArchSpec target_arch = target.GetArchitecture();
const llvm::Triple &target_triple = target_arch.GetTriple();
if (target_triple.getVendor() == llvm::Triple::Apple &&
(target_triple.getOS() == llvm::Triple::MacOSX ||
target_triple.getOS() == llvm::Triple::IOS ||
target_triple.getOS() == llvm::Triple::WatchOS ||
target_triple.getOS() == llvm::Triple::TvOS ||
target_triple.getOS() == llvm::Triple::BridgeOS ||
target_triple.getOS() == llvm::Triple::XROS)) {
bool make_core = false;
switch (target_arch.GetMachine()) {
case llvm::Triple::aarch64:
case llvm::Triple::aarch64_32:
case llvm::Triple::arm:
case llvm::Triple::thumb:
case llvm::Triple::x86:

bool ObjectFilePECOFF::SaveCore(const lldb::ProcessSP &process_sp,
lldb_private::SaveCoreOptions &options,
lldb_private::Status &error) {
// Outfile and process_sp are validated by PluginManager::SaveCore
assert(options.GetOutputFile().has_value());
assert(process_sp);
return SaveMiniDump(process_sp, options, error);
}

@DavidSpickett
Copy link
Collaborator

Ok so they aren't lying then. I suppose pecoff -> Windows and on Windows they use minidump among other things.

Comment on lines +850 to +858
// Report only for the plugin that was specified.
if (!plugin_name.empty())
return Status::FromErrorStringWithFormatv(
"The \"{}\" plugin is not able to save a core for this process.",
plugin_name);

return Status::FromErrorString(
"no ObjectFile plugins were able to save a core for this process");
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two errors don't have test coverage.

"The "{}" plugin is not able to save a core for this process."

To hit this one we'd need a plugin that can save a core, but not for this process.

"no ObjectFile plugins were able to save a core for this process"

Same thing but there needs to be > 1 plugin that tried.

And the original error doesn't have coverage either, presumably because no one has the appetite to inject a problem into a process. Which is fair enough.

So yes these don't have coverage but it's not the end of the world if someone gets the wrong one so I'm not going to demand tests for them.

Copy link
Collaborator

@DavidSpickett DavidSpickett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Fix the tab completion if you want to, and if you don't, raise an issue for it.

@da-viper da-viper merged commit 8d83d04 into llvm:main Jun 23, 2025
7 checks passed
Jaddyen pushed a commit to Jaddyen/llvm-project that referenced this pull request Jun 23, 2025
anthonyhatran pushed a commit to anthonyhatran/llvm-project that referenced this pull request Jun 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants