This is an archive of the discontinued LLVM Phabricator instance.

Two bug fixes for loading process save-core created core files, plus perf improvements
ClosedPublic

Authored by jasonmolenda on May 18 2023, 6:47 PM.

Details

Summary

I had two bugs with lldb's mach-o corefile loader using the "all image infos" LC_NOTE, and while working on those, I was looking closely at the code and found several perf mistakes where I would re-do work multiple times. This patch unfortunately ends up doing several things at once.

  1. If we load binaries in ObjectFileMachO via the "all image infos" LC_NOTE, ProcessMachCore::LoadBinariesViaMetadata() should detect that binaries were loaded by it, and not then scan for a dyld in the corefile, throw away the Target image list already constructed, and try to recreate it (and possibly not be successful). Also fix a mistake for the "main bin spec" LC_NOTE with userland process type in ProcessMachCore::LoadBinariesViaMetadata, noting that this is a userland dyld situation. It ProcessMachCore currently falls back to a scan, and finds dyld by exhaustive search, but that's unnecessary work.
  1. Binaries would first be loaded into the Target at their mach-header vm address, then ObjectFileMachO would load each binary's segments at the correct vmaddrs. With binaries in the Darwin shared cache, the actual layout in memory has TEXT and DATA in discontiguous vm ranges. But when we load all the segments in the Target at the mach header address contiguously, we get "address 0x... maps to more than one section" warnings. The addresses for the file would then be set correctly on a segment-by-segment basis in ObjectFileMachO, but the warnings were displayed to the user. For this, I add a set_address_in_target bool argument to DynamicLoader::LoadBinaryWithUUIDAndAddress to control whether it sets the address of the binary it finds in the Target, or if it leaves this to the caller. In ObjectFileMachO::LoadCoreFileImages when a binary has per-segment vmaddrs, it sets a local set_load_address and we pass that in to our calls. Update other callers to all pass set_load_address=true (e.g. ProcessGDBRemote::LoadStubBinaries) to DynamicLoader::LoadBinaryWithUUIDAndAddress to preserve existing behavior for them.
  1. Perf fix in ObjectFileMachO::LoadCoreFileImages where it would try to find the binary for a UUID multiple times as different approaches were unsuccessful. These attempts can be slow, and doing them multiple times can be more slow. Also a lot of code duplication if we have image.load_address or image.slide, but most lldb internal API take a uint64_t value and a "value is offset/slide" bool specifically to avoid this kind of duplication..
  1. A previously committed perf fix https://reviews.llvm.org/D150621 was also driven by this investigation, but that patch was easily chiseled off into a separate patch, so I did that one already.

Diff Detail

Event Timeline

jasonmolenda created this revision.May 18 2023, 6:47 PM
Herald added a project: Restricted Project. · View Herald TranscriptMay 18 2023, 6:47 PM
jasonmolenda requested review of this revision.May 18 2023, 6:47 PM

Bug fixes look good to me, the perf and code simplifications are also great to see. A few nits and I have one concern. Otherwise looks fine to me, I think.

lldb/include/lldb/Target/DynamicLoader.h
270–273

Just thinking aloud here, I wonder if we should construct some kind of "Options" struct to contain all of these bools?

lldb/source/Core/DynamicLoader.cpp
233–236

I think this technically changes behavior and probably not in a desirable way. Specifically, we previously only called DownloadObjectAndSymbolFile if force_symbol_search was true. Now you're passing it through which makes sense looking at the interface of DownloadObjectAndSymbolFile. However, peeking at the beginning of that function we see this code block:

// When dbgshell_command is empty, the user has not enabled the use of an
// external program to find the symbols, don't run it for them.
if (!force_lookup && dbgshell_command.empty())
  return false;

This means that even if force_lookup is false (or force_symbol_search as it's written here), if dbgshell_command is non-empty we'll still perform the lookup (assuming all the other checks pass) which is probably not what we intended performance-wise. That condition in the block above should probably be something like !force_lookup || dbgshell_command.empty() instead of &&.

267–269

You could use LLDB_LOG instead of LLDB_LOGF here and you'll get the file + function without explicitly writing it, but you'd have to change the formatting a bit since it uses Format and not Printf under the hood. Just a suggestion though.

276–278

Same here

285–287

Also here

lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
7021

nit: this can be const

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
1000

nit: , /* set_address_in_target */ true); (or something like this)

Or you can create a const bool like above with force_symbol_search and notify.

1031

Same here

lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
263–265

Same here

320–321

Same here

Thanks for the review comments, really helpful. I've also added a new block to DynamicLoader::LoadBinaryWithUUIDAndAddress to make the binary search system: (1) Target::GetOrCreateModule (lldb already has it), (2) Symbols::LocateExecutableObjectFile / Symbols::LocateExecutableSymbolFile (on a macOS system, this would be your Spotlight search), and then finally (3) Symbols::DownloadObjectAndSymbolFile with the force_symbol_search as passed in. I need to retest this with these different use cases to make sure it's all working correctly still, but looking pretty good now.

lldb/source/Core/DynamicLoader.cpp
233–236

Good catch. This is behavior I want, but it sure isn't real clear from the way this method is written. I will add some comments to make it clear that we want to do this external-program-lookup if either force_lookup is set, or if the user set dbgshell_command manually in their com.apple.DebugSymbols preferences.

lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
7021

it's not clearly written, this is actually an output parameter from Module::SetLoadAddress. The obvious next question is - why is it initialized. It should not be, I'll remove the initialization.

Updating the patch to incorporate Alex's feedback, and a little further refinement with testing. I am still doing final testing now, and mostly interested in how to create a mach-o corefile that would trigger the failure that started this fix - where lldb found enough of a dyld in a modified-memory-only corefile that it cleared the binary list, but not enough to actually load the full list of binaries out of corefile memory. I think it may be tricky to get a corefile that meets that criteria, but I want to look into it a bit.

I suspect the actual code changes are done at this point though.

bulbazord accepted this revision.May 30 2023, 2:18 PM

Looks like my concerns were addressed. Thanks!

This revision is now accepted and ready to land.May 30 2023, 2:18 PM