diff --git a/src/apps/debugger/debug_info/loading_state_handlers/DwarfLoadingStateHandler.cpp b/src/apps/debugger/debug_info/loading_state_handlers/DwarfLoadingStateHandler.cpp index 21b58b5dca..f174bfd11f 100644 --- a/src/apps/debugger/debug_info/loading_state_handlers/DwarfLoadingStateHandler.cpp +++ b/src/apps/debugger/debug_info/loading_state_handlers/DwarfLoadingStateHandler.cpp @@ -6,15 +6,32 @@ #include "DwarfLoadingStateHandler.h" +#include + #include #include +#include +#include +#include "AutoDeleter.h" #include "DwarfFile.h" #include "DwarfImageDebugInfoLoadingState.h" +#include "package/manager/PackageManager.h" #include "Tracing.h" #include "UserInterface.h" +using namespace BPackageKit; +using BPackageKit::BManager::BPrivate::BPackageManager; + + +enum { + USER_CHOICE_INSTALL_PACKAGE = 0, + USER_CHOICE_LOCATE_FILE , + USER_CHOICE_SKIP +}; + + DwarfLoadingStateHandler::DwarfLoadingStateHandler() : ImageDebugLoadingStateHandler() @@ -50,24 +67,143 @@ DwarfLoadingStateHandler::HandleState( DwarfFileLoadingState& fileState = dwarfState->GetFileState(); - // TODO: if the image in question is packaged, query if it has a - // corresponding debug info package. If so, offer to install it and - // then locate the file automatically rather than forcing the user to - // locate the file manually. - BString message; - message.SetToFormat("The debug information file '%s' for image '%s' is " - "missing. Please locate it if possible.", - fileState.externalInfoFileName.String(), fileState.dwarfFile->Name()); - int32 choice = interface->SynchronouslyAskUser("Debug info missing", - message.String(), "Locate", "Skip", NULL); + BString requiredPackage; + _GetMatchingDebugInfoPackage(fileState.externalInfoFileName, + requiredPackage); - if (choice == 0) { - entry_ref ref; - interface->SynchronouslyAskUserForFile(&ref); - BPath path(&ref); - if (path.InitCheck() == B_OK) - fileState.locatedExternalInfoPath = path.Path(); + // loop so that the user has a chance to retry or locate the file manually + // in case package installation fails, e.g. due to transient download + // issues. + for (;;) { + int32 choice; + BString message; + if (interface->IsInteractive()) { + if (requiredPackage.IsEmpty()) { + message.SetToFormat("The debug information file '%s' for " + "image '%s' is missing. Would you like to locate the file " + "manually?", fileState.externalInfoFileName.String(), + fileState.dwarfFile->Name()); + choice = interface->SynchronouslyAskUser("Debug info missing", + message.String(), "Locate", "Skip", NULL); + if (choice == 0) + choice = USER_CHOICE_LOCATE_FILE; + else if (choice == 1) + choice = USER_CHOICE_SKIP; + } else { + message.SetToFormat("The debug information file '%s' for " + "image '%s' is missing, but can be found in the package " + "'%s'. Would you like to install it, or locate the file " + "manually?", fileState.externalInfoFileName.String(), + fileState.dwarfFile->Name(), requiredPackage.String()); + choice = interface->SynchronouslyAskUser("Debug info missing", + message.String(), "Install", "Locate", "Skip"); + } + } else { + choice = requiredPackage.IsEmpty() + ? USER_CHOICE_SKIP : USER_CHOICE_INSTALL_PACKAGE; + } + + if (choice == USER_CHOICE_INSTALL_PACKAGE) { + // TODO: integrate the package installation functionality directly. + BString command; + command.SetToFormat("/bin/pkgman install -y %s", + requiredPackage.String()); + int error = system(command.String()); + if (interface->IsInteractive()) { + if (WIFEXITED(error)) { + error = WEXITSTATUS(error); + if (error == B_OK) + break; + message.SetToFormat("Package installation failed: %s.", + strerror(error)); + interface->NotifyUser("Error", message.String(), + USER_NOTIFICATION_ERROR); + continue; + } + } + break; + } else if (choice == USER_CHOICE_LOCATE_FILE) { + entry_ref ref; + interface->SynchronouslyAskUserForFile(&ref); + BPath path(&ref); + if (path.InitCheck() == B_OK) + fileState.locatedExternalInfoPath = path.Path(); + break; + } else + break; } fileState.state = DWARF_FILE_LOADING_STATE_USER_INPUT_PROVIDED; } + + +status_t +DwarfLoadingStateHandler::_GetMatchingDebugInfoPackage( + const BString& debugFileName, BString& _packageName) +{ + BString resolvableName; + BPackageVersion requiredVersion; + BPackageManager::ClientInstallationInterface clientInterface; + BPackageManager::UserInteractionHandler handler; + + BPackageManager packageManager(B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, + &clientInterface, &handler); + packageManager.Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES + | BPackageManager::B_ADD_REMOTE_REPOSITORIES); + BObjectList packages; + status_t error = _GetResolvableName(debugFileName, resolvableName, + requiredVersion); + if (error != B_OK) + return error; + + error = packageManager.Solver()->FindPackages(resolvableName, + BSolver::B_FIND_IN_PROVIDES, packages); + if (error != B_OK) + return error; + else if (packages.CountItems() == 0) + return B_ENTRY_NOT_FOUND; + + for (int32 i = 0; i < packages.CountItems(); i++) { + BSolverPackage* package = packages.ItemAt(i); + if (requiredVersion.Compare(package->Version()) == 0) { + _packageName = package->Name(); + return B_OK; + } + } + + return B_ENTRY_NOT_FOUND; +} + + +status_t +DwarfLoadingStateHandler::_GetResolvableName(const BString& debugFileName, + BString& _resolvableName, BPackageVersion& _resolvableVersion) +{ + BString fileName; + BString packageName; + BString packageVersion; + + int32 startIndex = 0; + int32 endIndex = debugFileName.FindFirst('('); + if (endIndex < 0) + return B_BAD_VALUE; + + debugFileName.CopyInto(fileName, 0, endIndex); + startIndex = endIndex + 1; + endIndex = debugFileName.FindFirst('-', startIndex); + if (endIndex < 0) + return B_BAD_VALUE; + + debugFileName.CopyInto(packageName, startIndex, endIndex - startIndex); + startIndex = endIndex + 1; + endIndex = debugFileName.FindFirst(')', startIndex); + if (endIndex < 0) + return B_BAD_VALUE; + + debugFileName.CopyInto(packageVersion, startIndex, + endIndex - startIndex); + + _resolvableName.SetToFormat("debuginfo:%s(%s)", fileName.String(), packageName.String()); + + return _resolvableVersion.SetTo(packageVersion); +} diff --git a/src/apps/debugger/debug_info/loading_state_handlers/DwarfLoadingStateHandler.h b/src/apps/debugger/debug_info/loading_state_handlers/DwarfLoadingStateHandler.h index 18bbb000a7..d3afcfaf58 100644 --- a/src/apps/debugger/debug_info/loading_state_handlers/DwarfLoadingStateHandler.h +++ b/src/apps/debugger/debug_info/loading_state_handlers/DwarfLoadingStateHandler.h @@ -9,6 +9,14 @@ #include "ImageDebugLoadingStateHandler.h" +namespace BPackageKit { + class BPackageVersion; +} + + +class BString; + + class DwarfLoadingStateHandler : public ImageDebugLoadingStateHandler { public: DwarfLoadingStateHandler(); @@ -21,6 +29,15 @@ public: SpecificImageDebugInfoLoadingState* state, UserInterface* interface); +private: + status_t _GetMatchingDebugInfoPackage( + const BString& debugFileName, + BString& _packageName); + + status_t _GetResolvableName(const BString& debugFileName, + BString& _resolvableName, + BPackageKit::BPackageVersion& + _resolvableVersion); };