Skip to content

Commit

Permalink
[lldb] add stop-at-user-entry option to process launch (#67019)
Browse files Browse the repository at this point in the history
## Description
This pull request adds a new `stop-at-user-entry` option to LLDB
`process launch` command, allowing users to launch a process and pause
execution at the entry point of the program (for C-based languages,
`main` function).

## Motivation
This option provides a convenient way to begin debugging a program by
launching it and breaking at the desired entry point.

## Changes Made
- Added `stop-at-user-entry` option to `Options.td` and the
corresponding case in `CommandOptionsProcessLaunch.cpp` (short option is
'm')
- Implemented `GetUserEntryPointName` method in the Language plugins
available at the moment.
- Declared the `CreateBreakpointAtUserEntry` method in the Target API.
- Create Shell test for the command
`command-process-launch-user-entry.test`.

## Usage
`process launch --stop-at-user-entry` or `process launch -m` launches
the process and pauses execution at the entry point of the program.
  • Loading branch information
junior-jl committed Oct 9, 2023
1 parent f92309a commit ac0dda8
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 3 deletions.
4 changes: 4 additions & 0 deletions lldb/include/lldb/Target/Language.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ class Language : public PluginInterface {

virtual lldb::LanguageType GetLanguageType() const = 0;

// Implement this function to return the user-defined entry point name
// for the language.
virtual llvm::StringRef GetUserEntryPointName() const { return {}; }

virtual bool IsTopLevelFunction(Function &function);

virtual bool IsSourceFile(llvm::StringRef file_path) const = 0;
Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/Target/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,8 @@ class Target : public std::enable_shared_from_this<Target>,

lldb::BreakpointSP GetBreakpointByID(lldb::break_id_t break_id);

lldb::BreakpointSP CreateBreakpointAtUserEntry(Status &error);

// Use this to create a file and line breakpoint to a given module or all
// module it is nullptr
lldb::BreakpointSP CreateBreakpoint(const FileSpecList *containingModules,
Expand Down
8 changes: 5 additions & 3 deletions lldb/source/Commands/CommandOptionsProcessLaunch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@ Status CommandOptionsProcessLaunch::SetOptionValue(
Status error;
const int short_option = g_process_launch_options[option_idx].short_option;

TargetSP target_sp =
execution_context ? execution_context->GetTargetSP() : TargetSP();
switch (short_option) {
case 's': // Stop at program entry point
launch_info.GetFlags().Set(eLaunchFlagStopAtEntry);
break;

case 'm': // Stop at user entry point
target_sp->CreateBreakpointAtUserEntry(error);
break;
case 'i': // STDIN for read only
{
FileAction action;
Expand Down Expand Up @@ -89,8 +93,6 @@ Status CommandOptionsProcessLaunch::SetOptionValue(
break;

case 'a': {
TargetSP target_sp =
execution_context ? execution_context->GetTargetSP() : TargetSP();
PlatformSP platform_sp =
target_sp ? target_sp->GetPlatform() : PlatformSP();
launch_info.GetArchitecture() =
Expand Down
4 changes: 4 additions & 0 deletions lldb/source/Commands/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,10 @@ let Command = "platform shell" in {
let Command = "process launch" in {
def process_launch_stop_at_entry : Option<"stop-at-entry", "s">,
Desc<"Stop at the entry point of the program when launching a process.">;
def process_launch_stop_at_user_entry : Option<"stop-at-user-entry", "m">,
Desc<"Stop at the user entry point when launching a process. For C based "
"languages this will be the 'main' function, but this might differ for "
"other languages.">;
def process_launch_disable_aslr : Option<"disable-aslr", "A">, Arg<"Boolean">,
Desc<"Set whether to disable address space layout randomization when launching a process.">;
def process_launch_plugin : Option<"plugin", "P">, Arg<"Plugin">,
Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ class CPlusPlusLanguage : public Language {
return lldb::eLanguageTypeC_plus_plus;
}

llvm::StringRef GetUserEntryPointName() const override { return "main"; }

std::unique_ptr<TypeScavenger> GetTypeScavenger() override;
lldb::TypeCategoryImplSP GetFormatters() override;

Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Plugins/Language/ObjC/ObjCLanguage.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ class ObjCLanguage : public Language {
return lldb::eLanguageTypeObjC;
}

llvm::StringRef GetUserEntryPointName() const override { return "main"; }

// Get all possible names for a method. Examples:
// If method_name is "+[NSString(my_additions) myStringWithCString:]"
// variant_names[0] => "+[NSString myStringWithCString:]"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class ObjCPlusPlusLanguage : public Language {
return lldb::eLanguageTypeObjC_plus_plus;
}

llvm::StringRef GetUserEntryPointName() const override { return "main"; }

llvm::StringRef GetNilReferenceSummaryString() override { return "nil"; }

bool IsSourceFile(llvm::StringRef file_path) const override;
Expand Down
39 changes: 39 additions & 0 deletions lldb/source/Target/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,45 @@ BreakpointSP Target::GetBreakpointByID(break_id_t break_id) {
return bp_sp;
}

lldb::BreakpointSP
lldb_private::Target::CreateBreakpointAtUserEntry(Status &error) {
ModuleSP main_module_sp = GetExecutableModule();
FileSpecList shared_lib_filter;
shared_lib_filter.Append(main_module_sp->GetFileSpec());
llvm::SetVector<std::string, std::vector<std::string>,
std::unordered_set<std::string>>
entryPointNamesSet;
for (LanguageType lang_type : Language::GetSupportedLanguages()) {
Language *lang = Language::FindPlugin(lang_type);
if (!lang) {
error.SetErrorString("Language not found\n");
return lldb::BreakpointSP();
}
std::string entryPointName = lang->GetUserEntryPointName().str();
if (!entryPointName.empty())
entryPointNamesSet.insert(entryPointName);
}
if (entryPointNamesSet.empty()) {
error.SetErrorString("No entry point name found\n");
return lldb::BreakpointSP();
}
BreakpointSP bp_sp = CreateBreakpoint(
&shared_lib_filter,
/*containingSourceFiles=*/nullptr, entryPointNamesSet.takeVector(),
/*func_name_type_mask=*/eFunctionNameTypeFull,
/*language=*/eLanguageTypeUnknown,
/*offset=*/0,
/*skip_prologue=*/eLazyBoolNo,
/*internal=*/false,
/*hardware=*/false);
if (!bp_sp) {
error.SetErrorString("Breakpoint creation failed.\n");
return lldb::BreakpointSP();
}
bp_sp->SetOneShot(true);
return bp_sp;
}

BreakpointSP Target::CreateSourceRegexBreakpoint(
const FileSpecList *containingModules,
const FileSpecList *source_file_spec_list,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# RUN: %clang_host -g %S/Inputs/main.c -o %t
# RUN: %lldb %t -s %s -o exit | FileCheck %s

process launch -m
# CHECK-LABEL: process launch -m
# CHECK: Process {{.*}} stopped
# CHECK: stop reason = one-shot breakpoint 1
# CHECK: frame #0: {{.*}}`main at main.c

0 comments on commit ac0dda8

Please sign in to comment.