From 9ddb0910a22432431a372066564dddda06e4724b Mon Sep 17 00:00:00 2001 From: Jim Ingham Date: Fri, 7 Aug 2020 14:44:01 -0700 Subject: [PATCH 01/25] Add a setting to force stepping to always run all threads. Also allow ScriptedThreadPlans to set & get their StopOthers state. Differential Revision: https://reviews.llvm.org/D85265 (cherry picked from commit d3dfd8cec44072302818c34193d898903dbaef8f) --- lldb/bindings/interface/SBThreadPlan.i | 8 +++ lldb/include/lldb/API/SBThreadPlan.h | 4 ++ lldb/include/lldb/Target/Process.h | 1 + lldb/include/lldb/Target/ThreadPlanPython.h | 5 +- lldb/source/API/SBThreadPlan.cpp | 19 +++++++ lldb/source/Commands/CommandObjectThread.cpp | 13 ++++- lldb/source/Target/Process.cpp | 6 ++ lldb/source/Target/TargetProperties.td | 3 + lldb/source/Target/Thread.cpp | 2 +- lldb/source/Target/ThreadPlanPython.cpp | 12 +--- .../functionalities/step_scripted/Steps.py | 22 ++++++- .../step_scripted/TestStepScripted.py | 57 ++++++++++++++++++- 12 files changed, 136 insertions(+), 16 deletions(-) diff --git a/lldb/bindings/interface/SBThreadPlan.i b/lldb/bindings/interface/SBThreadPlan.i index 36131d529b7b7..2003c6fdee3a3 100644 --- a/lldb/bindings/interface/SBThreadPlan.i +++ b/lldb/bindings/interface/SBThreadPlan.i @@ -92,6 +92,14 @@ public: bool IsPlanStale(); + %feature("docstring", "Return whether this plan will ask to stop other threads when it runs.") GetStopOthers; + bool + GetStopOthers(); + + %feature("docstring", "Set whether this plan will ask to stop other threads when it runs.") GetStopOthers; + void + SetStopOthers(bool stop_others); + SBThreadPlan QueueThreadPlanForStepOverRange (SBAddress &start_address, lldb::addr_t range_size); diff --git a/lldb/include/lldb/API/SBThreadPlan.h b/lldb/include/lldb/API/SBThreadPlan.h index 6639c10e437b3..0dc48437a6681 100644 --- a/lldb/include/lldb/API/SBThreadPlan.h +++ b/lldb/include/lldb/API/SBThreadPlan.h @@ -77,6 +77,10 @@ class LLDB_API SBThreadPlan { bool IsValid(); + bool GetStopOthers(); + + void SetStopOthers(bool stop_others); + // This section allows an SBThreadPlan to push another of the common types of // plans... SBThreadPlan QueueThreadPlanForStepOverRange(SBAddress &start_address, diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index bf9b64547ed50..2abedb8f6e2ef 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -91,6 +91,7 @@ class ProcessProperties : public Properties { std::chrono::seconds GetUtilityExpressionTimeout() const; bool GetOSPluginReportsAllThreads() const; void SetOSPluginReportsAllThreads(bool does_report); + bool GetSteppingRunsAllThreads() const; protected: Process *m_process; // Can be nullptr for global ProcessProperties diff --git a/lldb/include/lldb/Target/ThreadPlanPython.h b/lldb/include/lldb/Target/ThreadPlanPython.h index 27bf3a560b1ff..c04500ad5de85 100644 --- a/lldb/include/lldb/Target/ThreadPlanPython.h +++ b/lldb/include/lldb/Target/ThreadPlanPython.h @@ -45,7 +45,9 @@ class ThreadPlanPython : public ThreadPlan { bool WillStop() override; - bool StopOthers() override; + bool StopOthers() override { return m_stop_others; } + + void SetStopOthers(bool new_value) { m_stop_others = new_value; } void DidPush() override; @@ -67,6 +69,7 @@ class ThreadPlanPython : public ThreadPlan { std::string m_error_str; StructuredData::ObjectSP m_implementation_sp; bool m_did_push; + bool m_stop_others; ThreadPlanPython(const ThreadPlanPython &) = delete; const ThreadPlanPython &operator=(const ThreadPlanPython &) = delete; diff --git a/lldb/source/API/SBThreadPlan.cpp b/lldb/source/API/SBThreadPlan.cpp index c740c8b7492fc..9af673b0f3a99 100644 --- a/lldb/source/API/SBThreadPlan.cpp +++ b/lldb/source/API/SBThreadPlan.cpp @@ -196,6 +196,23 @@ bool SBThreadPlan::IsValid() { return false; } +bool SBThreadPlan::GetStopOthers() { + LLDB_RECORD_METHOD_NO_ARGS(bool, SBThreadPlan, GetStopOthers); + + ThreadPlanSP thread_plan_sp(GetSP()); + if (thread_plan_sp) + return thread_plan_sp->StopOthers(); + return false; +} + +void SBThreadPlan::SetStopOthers(bool stop_others) { + LLDB_RECORD_METHOD(void, SBThreadPlan, SetStopOthers, (bool), stop_others); + + ThreadPlanSP thread_plan_sp(GetSP()); + if (thread_plan_sp) + thread_plan_sp->SetStopOthers(stop_others); +} + // This section allows an SBThreadPlan to push another of the common types of // plans... // @@ -463,6 +480,8 @@ void RegisterMethods(Registry &R) { LLDB_REGISTER_METHOD(bool, SBThreadPlan, IsPlanComplete, ()); LLDB_REGISTER_METHOD(bool, SBThreadPlan, IsPlanStale, ()); LLDB_REGISTER_METHOD(bool, SBThreadPlan, IsValid, ()); + LLDB_REGISTER_METHOD(void, SBThreadPlan, SetStopOthers, (bool)); + LLDB_REGISTER_METHOD(bool, SBThreadPlan, GetStopOthers, ()); LLDB_REGISTER_METHOD(lldb::SBThreadPlan, SBThreadPlan, QueueThreadPlanForStepOverRange, (lldb::SBAddress &, lldb::addr_t)); diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index f0ad1798fec6a..666c208c32067 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -482,8 +482,16 @@ class ThreadStepScopeOptionGroup : public OptionGroup { // Check if we are in Non-Stop mode TargetSP target_sp = execution_context ? execution_context->GetTargetSP() : TargetSP(); - if (target_sp && target_sp->GetNonStopModeEnabled()) + if (target_sp && target_sp->GetNonStopModeEnabled()) { + // NonStopMode runs all threads by definition, so when it is on we don't + // need to check the process setting for runs all threads. m_run_mode = eOnlyThisThread; + } else { + ProcessSP process_sp = + execution_context ? execution_context->GetProcessSP() : ProcessSP(); + if (process_sp && process_sp->GetSteppingRunsAllThreads()) + m_run_mode = eAllThreads; + } m_avoid_regexp.clear(); m_step_in_target.clear(); @@ -612,8 +620,7 @@ class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed { if (m_options.m_run_mode == eAllThreads) bool_stop_other_threads = false; else if (m_options.m_run_mode == eOnlyDuringStepping) - bool_stop_other_threads = - (m_step_type != eStepTypeOut && m_step_type != eStepTypeScripted); + bool_stop_other_threads = (m_step_type != eStepTypeOut); else bool_stop_other_threads = true; diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 3776a90e546ae..d87f57f8ccacd 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -278,6 +278,12 @@ std::chrono::seconds ProcessProperties::GetUtilityExpressionTimeout() const { return std::chrono::seconds(value); } +bool ProcessProperties::GetSteppingRunsAllThreads() const { + const uint32_t idx = ePropertySteppingRunsAllThreads; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); +} + bool ProcessProperties::GetOSPluginReportsAllThreads() const { const bool fail_value = true; const Property *exp_property = diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td index 58aae058d5d43..7fb9b105ceefb 100644 --- a/lldb/source/Target/TargetProperties.td +++ b/lldb/source/Target/TargetProperties.td @@ -217,6 +217,9 @@ let Definition = "process" in { def UtilityExpressionTimeout: Property<"utility-expression-timeout", "UInt64">, DefaultUnsignedValue<15>, Desc<"The time in seconds to wait for LLDB-internal utility expressions.">; + def SteppingRunsAllThreads: Property<"run-all-threads", "Boolean">, + DefaultFalse, + Desc<"If true, stepping operations will run all threads. This is equivalent to setting the run-mode option to 'all-threads'.">; } let Definition = "platform" in { diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index 24cf4bf3ee1ed..94e8c560818d7 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -1380,7 +1380,7 @@ lldb::ThreadPlanSP Thread::QueueThreadPlanForStepScripted( ThreadPlanSP thread_plan_sp(new ThreadPlanPython(*this, class_name, extra_args_impl)); - + thread_plan_sp->SetStopOthers(stop_other_threads); status = QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } diff --git a/lldb/source/Target/ThreadPlanPython.cpp b/lldb/source/Target/ThreadPlanPython.cpp index 8171186319f5c..e83f0e9e715e4 100644 --- a/lldb/source/Target/ThreadPlanPython.cpp +++ b/lldb/source/Target/ThreadPlanPython.cpp @@ -25,11 +25,12 @@ using namespace lldb_private; // ThreadPlanPython -ThreadPlanPython::ThreadPlanPython(Thread &thread, const char *class_name, +ThreadPlanPython::ThreadPlanPython(Thread &thread, const char *class_name, StructuredDataImpl *args_data) : ThreadPlan(ThreadPlan::eKindPython, "Python based Thread Plan", thread, eVoteNoOpinion, eVoteNoOpinion), - m_class_name(class_name), m_args_data(args_data), m_did_push(false) { + m_class_name(class_name), m_args_data(args_data), m_did_push(false), + m_stop_others(false) { SetIsMasterPlan(true); SetOkayToDiscard(true); SetPrivate(false); @@ -162,13 +163,6 @@ lldb::StateType ThreadPlanPython::GetPlanRunState() { } // The ones below are not currently exported to Python. - -bool ThreadPlanPython::StopOthers() { - // For now Python plans run all threads, but we should add some controls for - // this. - return false; -} - void ThreadPlanPython::GetDescription(Stream *s, lldb::DescriptionLevel level) { s->Printf("Python thread plan implemented by class %s.", m_class_name.c_str()); diff --git a/lldb/test/API/functionalities/step_scripted/Steps.py b/lldb/test/API/functionalities/step_scripted/Steps.py index 4133cbbe6086a..d41d01d43095e 100644 --- a/lldb/test/API/functionalities/step_scripted/Steps.py +++ b/lldb/test/API/functionalities/step_scripted/Steps.py @@ -75,9 +75,29 @@ def should_stop(self, event): if not self.value.IsValid(): return True - print("Got next value: %d"%(self.value.GetValueAsUnsigned())) if not self.value.GetValueDidChange(): self.child_thread_plan = self.queue_child_thread_plan() return False else: return True + +# This plan does nothing, but sets stop_mode to the +# value of GetStopOthers for this plan. +class StepReportsStopOthers(): + stop_mode_dict = {} + + def __init__(self, thread_plan, args_data, dict): + self.thread_plan = thread_plan + self.key = args_data.GetValueForKey("token").GetStringValue(1000) + + def should_stop(self, event): + self.thread_plan.SetPlanComplete(True) + print("Called in should_stop") + StepReportsStopOthers.stop_mode_dict[self.key] = self.thread_plan.GetStopOthers() + return True + + def should_step(self): + return True + + def explains_stop(self, event): + return True diff --git a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py index 3a7665a34a3c4..cee0885da9c31 100644 --- a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py +++ b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py @@ -1,7 +1,7 @@ """ Tests stepping with scripted thread plans. """ - +import threading import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.decorators import * @@ -108,3 +108,58 @@ def do_test_checking_variable(self, use_cli): # And foo should have changed: self.assertTrue(foo_val.GetValueDidChange(), "Foo changed") + + def test_stop_others_from_command(self): + """Test that the stop-others flag is set correctly by the command line. + Also test that the run-all-threads property overrides this.""" + self.do_test_stop_others() + + def run_step(self, stop_others_value, run_mode, token): + import Steps + interp = self.dbg.GetCommandInterpreter() + result = lldb.SBCommandReturnObject() + + cmd = "thread step-scripted -C Steps.StepReportsStopOthers -k token -v %s"%(token) + if run_mode != None: + cmd = cmd + " --run-mode %s"%(run_mode) + print(cmd) + interp.HandleCommand(cmd, result) + self.assertTrue(result.Succeeded(), "Step scripted failed: %s."%(result.GetError())) + print(Steps.StepReportsStopOthers.stop_mode_dict) + value = Steps.StepReportsStopOthers.stop_mode_dict[token] + self.assertEqual(value, stop_others_value, "Stop others has the correct value.") + + def do_test_stop_others(self): + self.build() + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + "Set a breakpoint here", + self.main_source_file) + # First run with stop others false and see that we got that. + thread_id = "" + if sys.version_info.major == 2: + thread_id = str(threading._get_ident()) + else: + thread_id = str(threading.get_ident()) + + # all-threads should set stop others to False. + self.run_step(False, "all-threads", thread_id) + + # this-thread should set stop others to True + self.run_step(True, "this-thread", thread_id) + + # The default value should be stop others: + self.run_step(True, None, thread_id) + + # The target.process.run-all-threads should override this: + interp = self.dbg.GetCommandInterpreter() + result = lldb.SBCommandReturnObject() + + interp.HandleCommand("settings set target.process.run-all-threads true", result) + self.assertTrue(result.Succeeded, "setting run-all-threads works.") + + self.run_step(False, None, thread_id) + + + + + From 815b4c0df7080446775bf2c58867b8dedb9c5650 Mon Sep 17 00:00:00 2001 From: Jim Ingham Date: Thu, 13 Aug 2020 17:41:14 -0700 Subject: [PATCH 02/25] Add python enumerators for SBTypeEnumMemberList, and some tests for this API. Differential Revision: https://reviews.llvm.org/D85951 --- lldb/bindings/interface/SBTypeEnumMember.i | 39 +++++++++++++-- .../API/lang/c/enum_types/TestEnumTypes.py | 50 ++++++++++++++++--- lldb/test/API/lang/c/enum_types/main.c | 4 +- 3 files changed, 80 insertions(+), 13 deletions(-) diff --git a/lldb/bindings/interface/SBTypeEnumMember.i b/lldb/bindings/interface/SBTypeEnumMember.i index 006bdeaa8cee1..518e2bf90115a 100644 --- a/lldb/bindings/interface/SBTypeEnumMember.i +++ b/lldb/bindings/interface/SBTypeEnumMember.i @@ -71,10 +71,18 @@ protected: SBTypeEnumMember (const lldb::TypeEnumMemberImplSP &); }; -%feature( - "docstring", - "Represents a list of SBTypeEnumMembers." -) SBTypeEnumMemberList; +%feature("docstring", +"Represents a list of SBTypeEnumMembers. +SBTypeEnumMemberList supports SBTypeEnumMember iteration. +It also supports [] access either by index, or by enum +element name by doing: + + myType = target.FindFirstType('MyEnumWithElementA') + members = myType.GetEnumMembers() + first_elem = members[0] + elem_A = members['A'] + +") SBTypeEnumMemberList; class SBTypeEnumMemberList { @@ -99,6 +107,29 @@ public: uint32_t GetSize(); +#ifdef SWIGPYTHON + %pythoncode %{ + def __iter__(self): + '''Iterate over all members in a lldb.SBTypeEnumMemberList object.''' + return lldb_iter(self, 'GetSize', 'GetTypeEnumMemberAtIndex') + + def __len__(self): + '''Return the number of members in a lldb.SBTypeEnumMemberList object.''' + return self.GetSize() + + def __getitem__(self, key): + num_elements = self.GetSize() + if type(key) is int: + if key < num_elements: + return self.GetTypeEnumMemberAtIndex(key) + elif type(key) is str: + for idx in range(num_elements): + item = self.GetTypeEnumMemberAtIndex(idx) + if item.name == key: + return item + return None + %} +#endif private: std::unique_ptr m_opaque_ap; diff --git a/lldb/test/API/lang/c/enum_types/TestEnumTypes.py b/lldb/test/API/lang/c/enum_types/TestEnumTypes.py index cb5bb8eccaa29..0442dd34196a2 100644 --- a/lldb/test/API/lang/c/enum_types/TestEnumTypes.py +++ b/lldb/test/API/lang/c/enum_types/TestEnumTypes.py @@ -18,11 +18,9 @@ def setUp(self): # Find the line number to break inside main(). self.line = line_number('main.c', '// Set break point at this line.') - def test(self): - """Test 'image lookup -t days' and check for correct display and enum value printing.""" + def test_command_line(self): + """Test 'image lookup -t enum_test_days' and check for correct display and enum value printing.""" self.build() - exe = self.getBuildArtifact("a.out") - self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) lldbutil.run_to_source_breakpoint( self, '// Breakpoint for bitfield', lldb.SBFileSpec("main.c")) @@ -63,10 +61,10 @@ def test(self): self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, substrs=[' resolved, hit count = 1']) - # Look up information about the 'days' enum type. + # Look up information about the 'enum_test_days' enum type. # Check for correct display. - self.expect("image lookup -t days", DATA_TYPES_DISPLAYED_CORRECTLY, - substrs=['enum days {', + self.expect("image lookup -t enum_test_days", DATA_TYPES_DISPLAYED_CORRECTLY, + substrs=['enum enum_test_days {', 'Monday', 'Tuesday', 'Wednesday', @@ -124,3 +122,41 @@ def test(self): 'check for valid enumeration value', substrs=[enum_value]) lldbutil.continue_to_breakpoint(self.process(), bkpt) + + def check_enum_members(self, members): + name_matches = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", "kNumDays"] + value_matches = [-3, -2, -1, 0, 1, 2, 3, 4] + + # First test that the list of members from the type works + num_matches = len(name_matches) + self.assertEqual(len(members), num_matches, "enum_members returns the right number of elements") + for idx in range(0, num_matches): + member = members[idx] + self.assertTrue(member.IsValid(), "Got a valid member for idx: %d"%(idx)) + self.assertEqual(member.name, name_matches[idx], "Name matches for %d"%(idx)) + self.assertEqual(member.signed, value_matches[idx], "Value matches for %d"%(idx)) + + def test_api(self): + """Test the the SBTypeEnumMember API's work correctly for enum_test_days""" + self.build() + target = lldbutil.run_to_breakpoint_make_target(self) + + types = target.FindTypes("enum_test_days") + self.assertEqual(len(types), 1, "Found more than one enum_test_days type...") + type = types.GetTypeAtIndex(0) + + # First check using the Python list returned by the type: + self.check_enum_members(type.enum_members) + + # Now use the SBTypeEnumMemberList. + member_list = type.GetEnumMembers() + self.check_enum_members(member_list) + + # Now check that the by name accessor works: + for member in member_list: + name = member.name + check_member = member_list[name] + self.assertTrue(check_member.IsValid(), "Got a valid member for %s."%(name)) + self.assertEqual(name, check_member.name, "Got back the right name") + self.assertEqual(member.unsigned, check_member.unsigned) + diff --git a/lldb/test/API/lang/c/enum_types/main.c b/lldb/test/API/lang/c/enum_types/main.c index b866044e5e125..0aa73c970ec6f 100644 --- a/lldb/test/API/lang/c/enum_types/main.c +++ b/lldb/test/API/lang/c/enum_types/main.c @@ -25,7 +25,7 @@ int main (int argc, char const *argv[]) Beta = 4 }; - enum days { + enum enum_test_days { Monday = -3, Tuesday, Wednesday, @@ -40,7 +40,7 @@ int main (int argc, char const *argv[]) int nonsense = a + b + c + ab + ac + all; enum non_bitfield omega = Alpha | Beta; - enum days day; + enum enum_test_days day; struct foo f; f.op = NULL; // Breakpoint for bitfield for (day = Monday - 1; day <= kNumDays + 1; day++) From 823f635cb0f11ea7d4c36e4a3c309dc190c3e386 Mon Sep 17 00:00:00 2001 From: Jim Ingham Date: Mon, 21 Sep 2020 17:26:39 -0700 Subject: [PATCH 03/25] Fix reporting the lack of global variables in "target var". There was a little thinko which meant when stopped in a frame with debug information but whose CU didn't have any global variables we report: no debug info for frame This patch fixes that error message to say the intended: no global variables in current compile unit (cherry picked from commit 94b0d836a105116220052313df5a58473f706cdf) --- lldb/source/Commands/CommandObjectTarget.cpp | 1 + .../target_var/no_vars/Makefile | 3 +++ .../target_var/no_vars/TestTargetVarNoVars.py | 21 +++++++++++++++++++ .../functionalities/target_var/no_vars/main.c | 5 +++++ 4 files changed, 30 insertions(+) create mode 100644 lldb/test/API/functionalities/target_var/no_vars/Makefile create mode 100644 lldb/test/API/functionalities/target_var/no_vars/TestTargetVarNoVars.py create mode 100644 lldb/test/API/functionalities/target_var/no_vars/main.c diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index 9c0620092fb52..c6a7dd3c77baa 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -922,6 +922,7 @@ class CommandObjectTargetVariable : public CommandObjectParsed { CompileUnit *comp_unit = nullptr; if (frame) { SymbolContext sc = frame->GetSymbolContext(eSymbolContextCompUnit); + comp_unit = sc.comp_unit; if (sc.comp_unit) { const bool can_create = true; VariableListSP comp_unit_varlist_sp( diff --git a/lldb/test/API/functionalities/target_var/no_vars/Makefile b/lldb/test/API/functionalities/target_var/no_vars/Makefile new file mode 100644 index 0000000000000..10495940055b6 --- /dev/null +++ b/lldb/test/API/functionalities/target_var/no_vars/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/functionalities/target_var/no_vars/TestTargetVarNoVars.py b/lldb/test/API/functionalities/target_var/no_vars/TestTargetVarNoVars.py new file mode 100644 index 0000000000000..60ca8b1252b35 --- /dev/null +++ b/lldb/test/API/functionalities/target_var/no_vars/TestTargetVarNoVars.py @@ -0,0 +1,21 @@ +""" +Test that target var with no variables returns a correct error +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestTargetVarNoVars(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + NO_DEBUG_INFO_TESTCASE = True + + def test_target_var_no_vars(self): + self.build() + lldbutil.run_to_name_breakpoint(self, 'main') + self.expect("target variable", substrs=['no global variables in current compile unit', 'main.c'], error=True) + diff --git a/lldb/test/API/functionalities/target_var/no_vars/main.c b/lldb/test/API/functionalities/target_var/no_vars/main.c new file mode 100644 index 0000000000000..d7877b0a519bb --- /dev/null +++ b/lldb/test/API/functionalities/target_var/no_vars/main.c @@ -0,0 +1,5 @@ +int +main(int argc, char **argv) +{ + return argc; +} From e6f23784f6f498400a1a2dca2cc85ac9af3aea8d Mon Sep 17 00:00:00 2001 From: Jim Ingham Date: Tue, 22 Sep 2020 18:05:46 -0700 Subject: [PATCH 04/25] Add `breakpoint delete --disabled`: deletes all disabled breakpoints. Differential Revision: https://reviews.llvm.org/D88129 (cherry picked from commit 3726ac41e9e00d2f6f87779b68f91ea264223c8a) --- .../Commands/CommandObjectBreakpoint.cpp | 48 +++++++++++++++---- lldb/source/Commands/Options.td | 3 ++ .../TestBreakpointCommand.py | 30 ++++++++++++ 3 files changed, 73 insertions(+), 8 deletions(-) diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp index be7ef8a1b60bd..3ac3905befa10 100644 --- a/lldb/source/Commands/CommandObjectBreakpoint.cpp +++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp @@ -1407,7 +1407,8 @@ class CommandObjectBreakpointDelete : public CommandObjectParsed { class CommandOptions : public Options { public: - CommandOptions() : Options(), m_use_dummy(false), m_force(false) {} + CommandOptions() : Options(), m_use_dummy(false), m_force(false), + m_delete_disabled(false) {} ~CommandOptions() override = default; @@ -1424,6 +1425,10 @@ class CommandObjectBreakpointDelete : public CommandObjectParsed { case 'D': m_use_dummy = true; break; + + case 'd': + m_delete_disabled = true; + break; default: llvm_unreachable("Unimplemented option"); @@ -1435,6 +1440,7 @@ class CommandObjectBreakpointDelete : public CommandObjectParsed { void OptionParsingStarting(ExecutionContext *execution_context) override { m_use_dummy = false; m_force = false; + m_delete_disabled = false; } llvm::ArrayRef GetDefinitions() override { @@ -1444,16 +1450,18 @@ class CommandObjectBreakpointDelete : public CommandObjectParsed { // Instance variables to hold the values for command options. bool m_use_dummy; bool m_force; + bool m_delete_disabled; }; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target &target = GetSelectedOrDummyTarget(m_options.m_use_dummy); - + result.Clear(); + std::unique_lock lock; target.GetBreakpointList().GetListMutex(lock); - const BreakpointList &breakpoints = target.GetBreakpointList(); + BreakpointList &breakpoints = target.GetBreakpointList(); size_t num_breakpoints = breakpoints.GetSize(); @@ -1463,7 +1471,7 @@ class CommandObjectBreakpointDelete : public CommandObjectParsed { return false; } - if (command.empty()) { + if (command.empty() && !m_options.m_delete_disabled) { if (!m_options.m_force && !m_interpreter.Confirm( "About to delete all breakpoints, do you want to do that?", @@ -1479,10 +1487,34 @@ class CommandObjectBreakpointDelete : public CommandObjectParsed { } else { // Particular breakpoint selected; disable that breakpoint. BreakpointIDList valid_bp_ids; - CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, &target, result, &valid_bp_ids, - BreakpointName::Permissions::PermissionKinds::deletePerm); - + + if (m_options.m_delete_disabled) { + BreakpointIDList excluded_bp_ids; + + if (!command.empty()) { + CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( + command, &target, result, &excluded_bp_ids, + BreakpointName::Permissions::PermissionKinds::deletePerm); + } + for (auto breakpoint_sp : breakpoints.Breakpoints()) { + if (!breakpoint_sp->IsEnabled() && breakpoint_sp->AllowDelete()) { + BreakpointID bp_id(breakpoint_sp->GetID()); + size_t pos = 0; + if (!excluded_bp_ids.FindBreakpointID(bp_id, &pos)) + valid_bp_ids.AddBreakpointID(breakpoint_sp->GetID()); + } + } + if (valid_bp_ids.GetSize() == 0) { + result.AppendError("No disabled breakpoints."); + result.SetStatus(eReturnStatusFailed); + return false; + } + } else { + CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( + command, &target, result, &valid_bp_ids, + BreakpointName::Permissions::PermissionKinds::deletePerm); + } + if (result.Succeeded()) { int delete_count = 0; int disable_count = 0; diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index cdb102202edc5..5397c124ce4f7 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -224,6 +224,9 @@ let Command = "breakpoint delete" in { def breakpoint_delete_dummy_breakpoints : Option<"dummy-breakpoints", "D">, Group<1>, Desc<"Delete Dummy breakpoints - i.e. breakpoints set before a " "file is provided, which prime new targets.">; + def breakpoint_delete_disabled : Option<"disabled", "d">, Group<1>, + Desc<"Delete all breakpoints which are currently disabled. When using the disabled option " + "any breakpoints listed on the command line are EXCLUDED from deletion.">; } let Command = "breakpoint name" in { diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py b/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py index 9a66df9a798b7..69e8862808394 100644 --- a/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py @@ -287,3 +287,33 @@ def breakpoint_commands_on_creation(self): self.assertEqual(com_list.GetStringAtIndex(0), "bt", "First bt") self.assertEqual(com_list.GetStringAtIndex(1), "thread list", "Next thread list") self.assertEqual(com_list.GetStringAtIndex(2), "continue", "Last continue") + + def test_breakpoint_delete_disabled(self): + """Test 'break delete --disabled' works""" + self.build() + exe = self.getBuildArtifact("a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target.IsValid(), "Created an invalid target.") + + bp_1 = target.BreakpointCreateByName("main") + bp_2 = target.BreakpointCreateByName("not_here") + bp_3 = target.BreakpointCreateByName("main") + bp_3.AddName("DeleteMeNot") + + bp_1.SetEnabled(False) + bp_3.SetEnabled(False) + + bp_id_1 = bp_1.GetID() + bp_id_2 = bp_2.GetID() + bp_id_3 = bp_3.GetID() + + self.runCmd("breakpoint delete --disabled DeleteMeNot") + + bp_1 = target.FindBreakpointByID(bp_id_1) + self.assertFalse(bp_1.IsValid(), "Didn't delete disabled breakpoint 1") + + bp_2 = target.FindBreakpointByID(bp_id_2) + self.assertTrue(bp_2.IsValid(), "Deleted enabled breakpoint 2") + + bp_3 = target.FindBreakpointByID(bp_id_3) + self.assertTrue(bp_3.IsValid(), "DeleteMeNot didn't protect disabled breakpoint 3") From 3887971e27067b473b0fef7848a2a24cc032e80e Mon Sep 17 00:00:00 2001 From: Jim Ingham Date: Thu, 16 Jul 2020 11:34:50 -0700 Subject: [PATCH 05/25] Add an option (-y) to "break set" and "source list" that uses the same file:line:column form that we use to print out locations. Since we print them this way it makes sense we also accept that form. Differential Revision: https://reviews.llvm.org/D83975 (cherry picked from commit bc0a9a17a4a658153f4b524da3274d33a98d1c5b) --- lldb/include/lldb/Interpreter/OptionValue.h | 3 + .../Interpreter/OptionValueFileColonLine.h | 64 ++++++++ lldb/include/lldb/Interpreter/OptionValues.h | 1 + lldb/include/lldb/lldb-defines.h | 2 + lldb/include/lldb/lldb-enumerations.h | 1 + .../Python/lldbsuite/test/lldbutil.py | 36 +++++ .../Commands/CommandObjectBreakpoint.cpp | 18 ++- lldb/source/Commands/CommandObjectSource.cpp | 17 ++ lldb/source/Commands/Options.td | 17 +- lldb/source/Interpreter/CMakeLists.txt | 1 + lldb/source/Interpreter/CommandObject.cpp | 1 + lldb/source/Interpreter/OptionValue.cpp | 2 + lldb/source/Interpreter/OptionValueArray.cpp | 1 + .../Interpreter/OptionValueDictionary.cpp | 1 + .../Interpreter/OptionValueFileColonLine.cpp | 145 ++++++++++++++++++ .../Interpreter/OptionValueFileSpec.cpp | 7 - lldb/source/Interpreter/Property.cpp | 6 + .../breakpoint_by_file_colon_line/Makefile | 4 + .../TestBreakpointByFileColonLine.py | 42 +++++ .../breakpoint_by_file_colon_line/main.c | 14 ++ .../API/source-manager/TestSourceManager.py | 8 + lldb/unittests/Interpreter/CMakeLists.txt | 1 + .../TestOptionValueFileColonLine.cpp | 57 +++++++ 23 files changed, 436 insertions(+), 13 deletions(-) create mode 100644 lldb/include/lldb/Interpreter/OptionValueFileColonLine.h create mode 100644 lldb/source/Interpreter/OptionValueFileColonLine.cpp create mode 100644 lldb/test/API/functionalities/breakpoint/breakpoint_by_file_colon_line/Makefile create mode 100644 lldb/test/API/functionalities/breakpoint/breakpoint_by_file_colon_line/TestBreakpointByFileColonLine.py create mode 100644 lldb/test/API/functionalities/breakpoint/breakpoint_by_file_colon_line/main.c create mode 100644 lldb/unittests/Interpreter/TestOptionValueFileColonLine.cpp diff --git a/lldb/include/lldb/Interpreter/OptionValue.h b/lldb/include/lldb/Interpreter/OptionValue.h index 5b07427094bf2..27a5ddea116b6 100644 --- a/lldb/include/lldb/Interpreter/OptionValue.h +++ b/lldb/include/lldb/Interpreter/OptionValue.h @@ -31,6 +31,7 @@ class OptionValue { eTypeChar, eTypeDictionary, eTypeEnum, + eTypeFileLineColumn, eTypeFileSpec, eTypeFileSpecList, eTypeFormat, @@ -135,6 +136,8 @@ class OptionValue { return eTypeDictionary; case 1u << eTypeEnum: return eTypeEnum; + case 1u << eTypeFileLineColumn: + return eTypeFileLineColumn; case 1u << eTypeFileSpec: return eTypeFileSpec; case 1u << eTypeFileSpecList: diff --git a/lldb/include/lldb/Interpreter/OptionValueFileColonLine.h b/lldb/include/lldb/Interpreter/OptionValueFileColonLine.h new file mode 100644 index 0000000000000..6285c03afa149 --- /dev/null +++ b/lldb/include/lldb/Interpreter/OptionValueFileColonLine.h @@ -0,0 +1,64 @@ +//===-- OptionValueFileColonLine.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_INTERPRETER_OPTIONVALUEFILECOLONLINE_H +#define LLDB_INTERPRETER_OPTIONVALUEFILECOLONLINE_H + +#include "lldb/Interpreter/OptionValue.h" + +#include "lldb/Utility/FileSpec.h" +#include "llvm/Support/Chrono.h" + +namespace lldb_private { + +class OptionValueFileColonLine : public OptionValue { +public: + OptionValueFileColonLine(); + OptionValueFileColonLine(const llvm::StringRef input); + + ~OptionValueFileColonLine() override {} + + OptionValue::Type GetType() const override { return eTypeFileLineColumn; } + + void DumpValue(const ExecutionContext *exe_ctx, Stream &strm, + uint32_t dump_mask) override; + + Status + SetValueFromString(llvm::StringRef value, + VarSetOperationType op = eVarSetOperationAssign) override; + Status + SetValueFromString(const char *, + VarSetOperationType = eVarSetOperationAssign) = delete; + + bool Clear() override { + m_file_spec.Clear(); + m_line_number = LLDB_INVALID_LINE_NUMBER; + m_column_number = 0; + } + + lldb::OptionValueSP DeepCopy() const override; + + void AutoComplete(CommandInterpreter &interpreter, + CompletionRequest &request) override; + + FileSpec &GetFileSpec() { return m_file_spec; } + uint32_t GetLineNumber() { return m_line_number; } + uint32_t GetColumnNumber() { return m_column_number; } + + void SetCompletionMask(uint32_t mask) { m_completion_mask = mask; } + +protected: + FileSpec m_file_spec; + uint32_t m_line_number; + uint32_t m_column_number; + uint32_t m_completion_mask; +}; + +} // namespace lldb_private + +#endif // LLDB_INTERPRETER_OPTIONVALUEFILECOLONLINE_H diff --git a/lldb/include/lldb/Interpreter/OptionValues.h b/lldb/include/lldb/Interpreter/OptionValues.h index 36e7c192d60a3..6efc9e1ad064c 100644 --- a/lldb/include/lldb/Interpreter/OptionValues.h +++ b/lldb/include/lldb/Interpreter/OptionValues.h @@ -17,6 +17,7 @@ #include "lldb/Interpreter/OptionValueChar.h" #include "lldb/Interpreter/OptionValueDictionary.h" #include "lldb/Interpreter/OptionValueEnumeration.h" +#include "lldb/Interpreter/OptionValueFileColonLine.h" #include "lldb/Interpreter/OptionValueFileSpec.h" #include "lldb/Interpreter/OptionValueFileSpecList.h" #include "lldb/Interpreter/OptionValueFormat.h" diff --git a/lldb/include/lldb/lldb-defines.h b/lldb/include/lldb/lldb-defines.h index fea8079779a13..487cd0b01d5c5 100644 --- a/lldb/include/lldb/lldb-defines.h +++ b/lldb/include/lldb/lldb-defines.h @@ -95,6 +95,7 @@ #define LLDB_INVALID_SIGNAL_NUMBER INT32_MAX #define LLDB_INVALID_OFFSET UINT64_MAX // Must match max of lldb::offset_t #define LLDB_INVALID_LINE_NUMBER UINT32_MAX +#define LLDB_INVALID_COLUMN_NUMBER 0 #define LLDB_INVALID_QUEUE_ID 0 /// CPU Type definitions @@ -119,6 +120,7 @@ #define LLDB_OPT_SET_9 (1U << 8) #define LLDB_OPT_SET_10 (1U << 9) #define LLDB_OPT_SET_11 (1U << 10) +#define LLDB_OPT_SET_12 (1U << 11) #define LLDB_OPT_SET_FROM_TO(A, B) \ (((1U << (B)) - 1) ^ (((1U << (A)) - 1) >> 1)) diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 3ed3d0f74db22..107c3ffe8f955 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -529,6 +529,7 @@ enum CommandArgumentType { eArgTypeExpression, eArgTypeExpressionPath, eArgTypeExprFormat, + eArgTypeFileLineColumn, eArgTypeFilename, eArgTypeFormat, eArgTypeFrameIndex, diff --git a/lldb/packages/Python/lldbsuite/test/lldbutil.py b/lldb/packages/Python/lldbsuite/test/lldbutil.py index 2ba9ec4fcf0c4..a137a065ed7c9 100644 --- a/lldb/packages/Python/lldbsuite/test/lldbutil.py +++ b/lldb/packages/Python/lldbsuite/test/lldbutil.py @@ -539,6 +539,29 @@ def run_break_set_by_source_regexp( return get_bpno_from_match(break_results) +def run_break_set_by_file_colon_line( + test, + specifier, + path, + line_number, + column_number = 0, + extra_options=None, + num_expected_locations=-1): + command = 'breakpoint set -y "%s"'%(specifier) + if extra_options: + command += " " + extra_options + + print("About to run: '%s'", command) + break_results = run_break_set_command(test, command) + check_breakpoint_result( + test, + break_results, + num_locations = num_expected_locations, + file_name = path, + line_number = line_number, + column_number = column_number) + + return get_bpno_from_match(break_results) def run_break_set_command(test, command): """Run the command passed in - it must be some break set variant - and analyze the result. @@ -552,6 +575,7 @@ def run_break_set_command(test, command): If there is only one location, the dictionary MAY contain: file - source file name line_no - source line number + column - source column number symbol - symbol name inline_symbol - inlined symbol name offset - offset from the original symbol @@ -603,6 +627,7 @@ def check_breakpoint_result( break_results, file_name=None, line_number=-1, + column_number=0, symbol_name=None, symbol_match_exact=True, module_name=None, @@ -642,6 +667,17 @@ def check_breakpoint_result( (line_number, out_line_number)) + if column_number != 0: + out_column_number = 0 + if 'column' in break_results: + out_column_number = break_results['column'] + + test.assertTrue( + column_number == out_column_number, + "Breakpoint column number %s doesn't match resultant column %s." % + (column_number, + out_column_number)) + if symbol_name: out_symbol_name = "" # Look first for the inlined symbol name, otherwise use the symbol diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp index be7ef8a1b60bd..b62fe6c93cd8b 100644 --- a/lldb/source/Commands/CommandObjectBreakpoint.cpp +++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp @@ -17,6 +17,7 @@ #include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Interpreter/OptionGroupPythonClassWithDict.h" #include "lldb/Interpreter/OptionValueBoolean.h" +#include "lldb/Interpreter/OptionValueFileColonLine.h" #include "lldb/Interpreter/OptionValueString.h" #include "lldb/Interpreter/OptionValueUInt64.h" #include "lldb/Interpreter/Options.h" @@ -443,7 +444,22 @@ class CommandObjectBreakpointSet : public CommandObjectParsed { case 'X': m_source_regex_func_names.insert(std::string(option_arg)); break; - + + case 'y': + { + OptionValueFileColonLine value; + Status fcl_err = value.SetValueFromString(option_arg); + if (!fcl_err.Success()) { + error.SetErrorStringWithFormat( + "Invalid value for file:line specifier: %s", + fcl_err.AsCString()); + } else { + m_filenames.AppendIfUnique(value.GetFileSpec()); + m_line_num = value.GetLineNumber(); + m_column = value.GetColumnNumber(); + } + } break; + default: llvm_unreachable("Unimplemented option"); } diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp index 1ccfd3a5166f4..8fff22a06366c 100644 --- a/lldb/source/Commands/CommandObjectSource.cpp +++ b/lldb/source/Commands/CommandObjectSource.cpp @@ -16,6 +16,7 @@ #include "lldb/Host/OptionParser.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionValueFileColonLine.h" #include "lldb/Interpreter/Options.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Function.h" @@ -667,6 +668,22 @@ class CommandObjectSourceList : public CommandObjectParsed { case 'r': reverse = true; break; + case 'y': + { + OptionValueFileColonLine value; + Status fcl_err = value.SetValueFromString(option_arg); + if (!fcl_err.Success()) { + error.SetErrorStringWithFormat( + "Invalid value for file:line specifier: %s", + fcl_err.AsCString()); + } else { + file_name = value.GetFileSpec().GetPath(); + start_line = value.GetLineNumber(); + // I don't see anything useful to do with a column number, but I don't + // want to complain since someone may well have cut and pasted a + // listing from somewhere that included a column. + } + } break; default: llvm_unreachable("Unimplemented option"); } diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index cdb102202edc5..a2f2a8e843d84 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -105,7 +105,7 @@ let Command = "breakpoint dummy" in { let Command = "breakpoint set" in { def breakpoint_set_shlib : Option<"shlib", "s">, Arg<"ShlibName">, - Completion<"Module">, Groups<[1,2,3,4,5,6,7,8,9,11]>, // *not* in group 10 + Completion<"Module">, Groups<[1,2,3,4,5,6,7,8,9,11,12]>, // *not* in group 10 Desc<"Set the breakpoint only in this shared library. Can repeat this " "option multiple times to specify multiple shared libraries.">; def breakpoint_set_hardware : Option<"hardware", "H">, @@ -186,21 +186,24 @@ let Command = "breakpoint set" in { "expression (note: currently only implemented for setting breakpoints on " "identifiers). If not set the target.language setting is used.">; def breakpoint_set_skip_prologue : Option<"skip-prologue", "K">, - Arg<"Boolean">, Groups<[1,3,4,5,6,7,8]>, + Arg<"Boolean">, Groups<[1,3,4,5,6,7,8,12]>, Desc<"sKip the prologue if the breakpoint is at the beginning of a " "function. If not set the target.skip-prologue setting is used.">; def breakpoint_set_breakpoint_name : Option<"breakpoint-name", "N">, Arg<"BreakpointName">, Desc<"Adds this to the list of names for this breakpoint.">; def breakpoint_set_address_slide : Option<"address-slide", "R">, - Arg<"Address">, Groups<[1,3,4,5,6,7,8]>, + Arg<"Address">, Groups<[1,3,4,5,6,7,8,12]>, Desc<"Add the specified offset to whatever address(es) the breakpoint " "resolves to. At present this applies the offset directly as given, and " "doesn't try to align it to instruction boundaries.">; def breakpoint_set_move_to_nearest_code : Option<"move-to-nearest-code", "m">, - Groups<[1, 9]>, Arg<"Boolean">, + Groups<[1,9,12]>, Arg<"Boolean">, Desc<"Move breakpoints to nearest code. If not set the " "target.move-to-nearest-codesetting is used.">; + def breakpoint_set_file_colon_line : Option<"joint-specifier", "y">, Group<12>, Arg<"FileLineColumn">, + Required, Completion<"SourceFile">, + Desc<"A specifier in the form filename:line[:column] for setting file & line breakpoints.">; /* Don't add this option till it actually does something useful... def breakpoint_set_exception_typename : Option<"exception-typename", "O">, Arg<"TypeName">, Desc<"The breakpoint will only stop if an " @@ -737,7 +740,7 @@ let Command = "source info" in { let Command = "source list" in { def source_list_count : Option<"count", "c">, Arg<"Count">, Desc<"The number of source lines to display.">; - def source_list_shlib : Option<"shlib", "s">, Groups<[1,2]>, Arg<"ShlibName">, + def source_list_shlib : Option<"shlib", "s">, Groups<[1,2,5]>, Arg<"ShlibName">, Completion<"Module">, Desc<"Look up the source file in the given shared library.">; def source_list_show_breakpoints : Option<"show-breakpoints", "b">, @@ -755,6 +758,10 @@ let Command = "source list" in { " information for the corresponding file and line.">; def source_list_reverse : Option<"reverse", "r">, Group<4>, Desc<"Reverse the" " listing to look backwards from the last displayed block of source.">; + def source_list_file_colon_line : Option<"joint-specifier", "y">, Group<5>, + Arg<"FileLineColumn">, Completion<"SourceFile">, + Desc<"A specifier in the form filename:line[:column] from which to display" + " source.">; } let Command = "target dependents" in { diff --git a/lldb/source/Interpreter/CMakeLists.txt b/lldb/source/Interpreter/CMakeLists.txt index 0ed39869467ec..7a8c826d040cb 100644 --- a/lldb/source/Interpreter/CMakeLists.txt +++ b/lldb/source/Interpreter/CMakeLists.txt @@ -35,6 +35,7 @@ add_lldb_library(lldbInterpreter OptionValueChar.cpp OptionValueDictionary.cpp OptionValueEnumeration.cpp + OptionValueFileColonLine.cpp OptionValueFileSpec.cpp OptionValueFileSpecList.cpp OptionValueFormat.cpp diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp index 538f7a1ba6930..6f58b8ba0ea77 100644 --- a/lldb/source/Interpreter/CommandObject.cpp +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -1064,6 +1064,7 @@ CommandObject::ArgumentTableEntry CommandObject::g_arguments_data[] = { { eArgTypeIndex, "index", CommandCompletions::eNoCompletion, { nullptr, false }, "An index into a list." }, { eArgTypeLanguage, "source-language", CommandCompletions::eNoCompletion, { LanguageTypeHelpTextCallback, true }, nullptr }, { eArgTypeLineNum, "linenum", CommandCompletions::eNoCompletion, { nullptr, false }, "Line number in a source file." }, + { eArgTypeFileLineColumn, "linespec", CommandCompletions::eNoCompletion, { nullptr, false }, "A source specifier in the form file:line[:column]" }, { eArgTypeLogCategory, "log-category", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a category within a log channel, e.g. all (try \"log list\" to see a list of all channels and their categories." }, { eArgTypeLogChannel, "log-channel", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a log channel, e.g. process.gdb-remote (try \"log list\" to see a list of all channels and their categories)." }, { eArgTypeMethod, "method", CommandCompletions::eNoCompletion, { nullptr, false }, "A C++ method name." }, diff --git a/lldb/source/Interpreter/OptionValue.cpp b/lldb/source/Interpreter/OptionValue.cpp index 2e3974b310198..0bd9a591af678 100644 --- a/lldb/source/Interpreter/OptionValue.cpp +++ b/lldb/source/Interpreter/OptionValue.cpp @@ -471,6 +471,8 @@ const char *OptionValue::GetBuiltinTypeAsCString(Type t) { return "dictionary"; case eTypeEnum: return "enum"; + case eTypeFileLineColumn: + return "file:line:column specifier"; case eTypeFileSpec: return "file"; case eTypeFileSpecList: diff --git a/lldb/source/Interpreter/OptionValueArray.cpp b/lldb/source/Interpreter/OptionValueArray.cpp index 9be11e32e2dbc..0b293ccfc248f 100644 --- a/lldb/source/Interpreter/OptionValueArray.cpp +++ b/lldb/source/Interpreter/OptionValueArray.cpp @@ -52,6 +52,7 @@ void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, case eTypeChar: case eTypeEnum: case eTypeFileSpec: + case eTypeFileLineColumn: case eTypeFormat: case eTypeSInt64: case eTypeString: diff --git a/lldb/source/Interpreter/OptionValueDictionary.cpp b/lldb/source/Interpreter/OptionValueDictionary.cpp index caadccd042329..79323f502d179 100644 --- a/lldb/source/Interpreter/OptionValueDictionary.cpp +++ b/lldb/source/Interpreter/OptionValueDictionary.cpp @@ -62,6 +62,7 @@ void OptionValueDictionary::DumpValue(const ExecutionContext *exe_ctx, case eTypeBoolean: case eTypeChar: case eTypeEnum: + case eTypeFileLineColumn: case eTypeFileSpec: case eTypeFormat: case eTypeSInt64: diff --git a/lldb/source/Interpreter/OptionValueFileColonLine.cpp b/lldb/source/Interpreter/OptionValueFileColonLine.cpp new file mode 100644 index 0000000000000..dac557c4248aa --- /dev/null +++ b/lldb/source/Interpreter/OptionValueFileColonLine.cpp @@ -0,0 +1,145 @@ +//===-- OptionValueFileColonLine.cpp---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueFileColonLine.h" + +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/State.h" + +using namespace lldb; +using namespace lldb_private; + +// This is an OptionValue for parsing file:line:column specifications. +// I set the completer to "source file" which isn't quite right, but we can +// only usefully complete in the file name part of it so it should be good +// enough. +OptionValueFileColonLine::OptionValueFileColonLine() + : OptionValue(), m_file_spec(), m_line_number(LLDB_INVALID_LINE_NUMBER), + m_column_number(LLDB_INVALID_COLUMN_NUMBER), + m_completion_mask(CommandCompletions::eSourceFileCompletion) {} + +OptionValueFileColonLine::OptionValueFileColonLine(llvm::StringRef input) + : OptionValue(), m_file_spec(), m_line_number(LLDB_INVALID_LINE_NUMBER), + m_column_number(LLDB_INVALID_COLUMN_NUMBER), + m_completion_mask(CommandCompletions::eSourceFileCompletion) { + SetValueFromString(input, eVarSetOperationAssign); +} + +void OptionValueFileColonLine::DumpValue(const ExecutionContext *exe_ctx, + Stream &strm, uint32_t dump_mask) { + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + if (dump_mask & eDumpOptionValue) { + if (dump_mask & eDumpOptionType) + strm.PutCString(" = "); + + if (m_file_spec) + strm << '"' << m_file_spec.GetPath().c_str() << '"'; + if (m_line_number != LLDB_INVALID_LINE_NUMBER) + strm.Printf(":%d", m_line_number); + if (m_column_number != LLDB_INVALID_COLUMN_NUMBER) + strm.Printf(":%d", m_column_number); + } +} + +Status OptionValueFileColonLine::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + Status error; + switch (op) { + case eVarSetOperationClear: + Clear(); + NotifyValueChanged(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + if (value.size() > 0) { + // This is in the form filename:linenumber:column. + // I wish we could use filename:linenumber.column, that would make the + // parsing unambiguous and so much easier... + // But clang & gcc both print the output with two : so we're stuck with + // the two colons. Practically, the only actual ambiguity this introduces + // is with files like "foo:10", which doesn't seem terribly likely. + + // Providing the column is optional, so the input value might have one or + // two colons. First pick off the last colon separated piece. + // It has to be there, since the line number is required: + llvm::StringRef last_piece; + llvm::StringRef left_of_last_piece; + + std::tie(left_of_last_piece, last_piece) = value.rsplit(':'); + if (last_piece.empty()) { + error.SetErrorStringWithFormat("Line specifier must include file and " + "line: '%s'", + value.str().c_str()); + return error; + } + + // Now see if there's another colon and if so pull out the middle piece: + // Then check whether the middle piece is an integer. If it is, then it + // was the line number, and if it isn't we're going to assume that there + // was a colon in the filename (see note at the beginning of the function) + // and ignore it. + llvm::StringRef file_name; + llvm::StringRef middle_piece; + + std::tie(file_name, middle_piece) = left_of_last_piece.rsplit(':'); + if (middle_piece.empty() || !llvm::to_integer(middle_piece, + m_line_number)) { + // The middle piece was empty or not an integer, so there were only two + // legit pieces; our original division was right. Reassign the file + // name and pull out the line number: + file_name = left_of_last_piece; + if (!llvm::to_integer(last_piece, m_line_number)) { + error.SetErrorStringWithFormat("Bad line number value '%s' in: '%s'", + last_piece.str().c_str(), + value.str().c_str()); + return error; + } + } else { + // There were three pieces, and we've got the line number. So now + // we just need to check the column number which was the last peice. + if (!llvm::to_integer(last_piece, m_column_number)) { + error.SetErrorStringWithFormat("Bad column value '%s' in: '%s'", + last_piece.str().c_str(), + value.str().c_str()); + return error; + } + } + + m_value_was_set = true; + m_file_spec.SetFile(file_name, FileSpec::Style::native); + NotifyValueChanged(); + } else { + error.SetErrorString("invalid value string"); + } + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromString(value, op); + break; + } + return error; +} + +lldb::OptionValueSP OptionValueFileColonLine::DeepCopy() const { + return OptionValueSP(new OptionValueFileColonLine(*this)); +} + +void OptionValueFileColonLine::AutoComplete(CommandInterpreter &interpreter, + CompletionRequest &request) { + CommandCompletions::InvokeCommonCompletionCallbacks( + interpreter, m_completion_mask, request, nullptr); +} diff --git a/lldb/source/Interpreter/OptionValueFileSpec.cpp b/lldb/source/Interpreter/OptionValueFileSpec.cpp index 15acb7e5e5b08..a03fd55d0385a 100644 --- a/lldb/source/Interpreter/OptionValueFileSpec.cpp +++ b/lldb/source/Interpreter/OptionValueFileSpec.cpp @@ -64,13 +64,6 @@ Status OptionValueFileSpec::SetValueFromString(llvm::StringRef value, case eVarSetOperationReplace: case eVarSetOperationAssign: if (value.size() > 0) { - // The setting value may have whitespace, double-quotes, or single-quotes - // around the file path to indicate that internal spaces are not word - // breaks. Strip off any ws & quotes from the start and end of the file - // path - we aren't doing any word // breaking here so the quoting is - // unnecessary. NB this will cause a problem if someone tries to specify - // a file path that legitimately begins or ends with a " or ' character, - // or whitespace. value = value.trim("\"' \t"); m_value_was_set = true; m_current_value.SetFile(value.str(), FileSpec::Style::native); diff --git a/lldb/source/Interpreter/Property.cpp b/lldb/source/Interpreter/Property.cpp index 9238112498533..a02497662ff5f 100644 --- a/lldb/source/Interpreter/Property.cpp +++ b/lldb/source/Interpreter/Property.cpp @@ -99,6 +99,12 @@ Property::Property(const PropertyDefinition &definition) } break; + case OptionValue::eTypeFileLineColumn: + // "definition.default_uint_value" is not used for a + // OptionValue::eTypeFileSpecList + m_value_sp = std::make_shared(); + break; + case OptionValue::eTypeFileSpec: { // "definition.default_uint_value" represents if the // "definition.default_cstr_value" should be resolved or not diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_by_file_colon_line/Makefile b/lldb/test/API/functionalities/breakpoint/breakpoint_by_file_colon_line/Makefile new file mode 100644 index 0000000000000..ad42b20df4ed5 --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_by_file_colon_line/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := main.c +CFLAGS_EXTRAS := -std=c99 -gcolumn-info + +include Makefile.rules diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_by_file_colon_line/TestBreakpointByFileColonLine.py b/lldb/test/API/functionalities/breakpoint/breakpoint_by_file_colon_line/TestBreakpointByFileColonLine.py new file mode 100644 index 0000000000000..011fcdce8da46 --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_by_file_colon_line/TestBreakpointByFileColonLine.py @@ -0,0 +1,42 @@ +""" +Test setting a breakpoint by line and column. +""" + + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class BreakpointByLineAndColumnTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def testBreakpointSpecWithLine(self): + self.build() + exe = self.getBuildArtifact("a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # This one should work: + lldbutil.run_break_set_by_file_colon_line(self, "main.c:11", "main.c", 11, num_expected_locations = 1) + # Let's try an illegal specifier to make sure the command fails. I'm not being exhaustive + # since the UnitTest has more bad patterns. I'm just testing that if the SetFromString + # fails, we propagate the error. + self.expect("break set -y 'foo.c'", error=True) + + ## Skip gcc version less 7.1 since it doesn't support -gcolumn-info + @skipIf(compiler="gcc", compiler_version=['<', '7.1']) + def testBreakpointByLine(self): + self.build() + exe = self.getBuildArtifact("a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + main_c = lldb.SBFileSpec("main.c") + lldbutil.run_break_set_by_file_colon_line(self, "main.c:11:50", "main.c", 11, num_expected_locations = 1) + diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_by_file_colon_line/main.c b/lldb/test/API/functionalities/breakpoint/breakpoint_by_file_colon_line/main.c new file mode 100644 index 0000000000000..6e3f7e2ddbf93 --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_by_file_colon_line/main.c @@ -0,0 +1,14 @@ +int square(int x) +{ + return x * x; +} + +int main (int argc, char const *argv[]) +{ + int did_call = 0; + + // Line 11. v Column 50. + if(square(argc+1) != 0) { did_call = 1; return square(argc); } + // ^ + return square(0); +} diff --git a/lldb/test/API/source-manager/TestSourceManager.py b/lldb/test/API/source-manager/TestSourceManager.py index 714b736da02e6..3ce056f2d0294 100644 --- a/lldb/test/API/source-manager/TestSourceManager.py +++ b/lldb/test/API/source-manager/TestSourceManager.py @@ -197,6 +197,14 @@ def test_modify_source_file_while_debugging(self): SOURCE_DISPLAYED_CORRECTLY, substrs=['Hello world']) + # Do the same thing with a file & line spec: + self.expect( + "source list -y main-copy.c:%d" % + self.line, + SOURCE_DISPLAYED_CORRECTLY, + substrs=['Hello world']) + + # The '-b' option shows the line table locations from the debug information # that indicates valid places to set source level breakpoints. diff --git a/lldb/unittests/Interpreter/CMakeLists.txt b/lldb/unittests/Interpreter/CMakeLists.txt index 0de5b0b72488e..28663ec02a2a9 100644 --- a/lldb/unittests/Interpreter/CMakeLists.txt +++ b/lldb/unittests/Interpreter/CMakeLists.txt @@ -1,6 +1,7 @@ add_lldb_unittest(InterpreterTests TestCompletion.cpp TestOptionArgParser.cpp + TestOptionValueFileColonLine.cpp LINK_LIBS lldbInterpreter diff --git a/lldb/unittests/Interpreter/TestOptionValueFileColonLine.cpp b/lldb/unittests/Interpreter/TestOptionValueFileColonLine.cpp new file mode 100644 index 0000000000000..e5b2c77a16162 --- /dev/null +++ b/lldb/unittests/Interpreter/TestOptionValueFileColonLine.cpp @@ -0,0 +1,57 @@ +//===-- ArgsTest.cpp ------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueFileColonLine.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Status.h" +#include "gtest/gtest.h" + +using namespace lldb_private; + +void CheckSetting(const char *input, bool success, const char *path = nullptr, + uint32_t line_number = LLDB_INVALID_ADDRESS, + uint32_t column_number = 0) { + + OptionValueFileColonLine value; + Status error; + llvm::StringRef s_ref(input); + error = value.SetValueFromString(s_ref); + ASSERT_EQ(error.Success(), success); + + // If we were meant to fail, we don't need to do more checks: + if (!success) + return; + + ASSERT_EQ(value.GetLineNumber(), line_number); + ASSERT_EQ(value.GetColumnNumber(), column_number); + std::string value_path = value.GetFileSpec().GetPath(); + ASSERT_STREQ(value_path.c_str(), path); +} + +TEST(OptionValueFileColonLine, setFromString) { + OptionValueFileColonLine value; + Status error; + + // Make sure a default constructed value is invalid: + ASSERT_EQ(value.GetLineNumber(), LLDB_INVALID_LINE_NUMBER); + ASSERT_EQ(value.GetColumnNumber(), 0); + ASSERT_FALSE(value.GetFileSpec()); + + // Make sure it is an error to pass a specifier with no line number: + CheckSetting("foo.c", false); + + // Now try with just a file & line: + CheckSetting("foo.c:12", true, "foo.c", 12); + CheckSetting("foo.c:12:20", true, "foo.c", 12, 20); + // Make sure a colon doesn't mess us up: + CheckSetting("foo:bar.c:12", true, "foo:bar.c", 12); + CheckSetting("foo:bar.c:12:20", true, "foo:bar.c", 12, 20); + // Try errors in the line number: + CheckSetting("foo.c:12c", false); + CheckSetting("foo.c:12:20c", false); +} From 271da2aac13de3f3e34f43ae7308f5505b8164b6 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Thu, 24 Sep 2020 16:59:36 -0700 Subject: [PATCH 06/25] Add a verifier check that rejects non-distinct DISubprogram function attachments. They would crash the backend, which expects all DISubprograms that are not part of the type system to have a unit field. Clang right before https://reviews.llvm.org/D79967 would generate this kind of broken IR. rdar://problem/69534688 (cherry picked from commit e17f52d623cc146b7d9bf5a2e02965043508b4c4) --- llvm/lib/IR/Verifier.cpp | 4 ++++ .../Generic/2009-11-03-InsertExtractValue.ll | 2 +- llvm/test/Verifier/unique-disubprogram.ll | 16 ++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 llvm/test/Verifier/unique-disubprogram.ll diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index f81e947dc94c2..2e9c2e4af90df 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -2404,6 +2404,10 @@ void Verifier::visitFunction(const Function &F) { "function must have a single !dbg attachment", &F, I.second); AssertDI(isa(I.second), "function !dbg attachment must be a subprogram", &F, I.second); + AssertDI(cast(I.second)->isDistinct(), + "function definition may only have a distinct !dbg attachment", + &F); + auto *SP = cast(I.second); const Function *&AttachedTo = DISubprogramAttachments[SP]; AssertDI(!AttachedTo || AttachedTo == &F, diff --git a/llvm/test/DebugInfo/Generic/2009-11-03-InsertExtractValue.ll b/llvm/test/DebugInfo/Generic/2009-11-03-InsertExtractValue.ll index 57ee7ebbb2cb3..81bb5f2457ed9 100644 --- a/llvm/test/DebugInfo/Generic/2009-11-03-InsertExtractValue.ll +++ b/llvm/test/DebugInfo/Generic/2009-11-03-InsertExtractValue.ll @@ -3,7 +3,7 @@ !llvm.module.flags = !{!6} !llvm.dbg.cu = !{!5} -!0 = !DISubprogram(name: "bar", linkageName: "_ZN3foo3barEv", line: 3, isLocal: false, isDefinition: false, virtualIndex: 6, flags: DIFlagProtected | DIFlagPrototyped, isOptimized: false, scopeLine: 3, file: !4, scope: !1, type: !2) +!0 = distinct !DISubprogram(name: "bar", linkageName: "_ZN3foo3barEv", line: 3, isLocal: false, isDefinition: false, virtualIndex: 6, flags: DIFlagProtected | DIFlagPrototyped, isOptimized: false, scopeLine: 3, file: !4, scope: !1, type: !2) !1 = !DIFile(filename: "/foo", directory: "bar.cpp") !2 = !DISubroutineType(types: !3) !3 = !{null} diff --git a/llvm/test/Verifier/unique-disubprogram.ll b/llvm/test/Verifier/unique-disubprogram.ll new file mode 100644 index 0000000000000..b4789a9293f7b --- /dev/null +++ b/llvm/test/Verifier/unique-disubprogram.ll @@ -0,0 +1,16 @@ +; RUN: llvm-as -disable-output <%s 2>&1| FileCheck %s +define i32 @_Z3foov() local_unnamed_addr !dbg !9 { + ret i32 5 +} +!llvm.module.flags = !{!2} +!llvm.dbg.cu = !{!5} +!llvm.linker.options = !{} + +!2 = !{i32 2, !"Debug Info Version", i32 3} +!5 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !6, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug) +!6 = !DIFile(filename: "t.cpp", directory: "/") +!7 = !{} +; CHECK: function definition may only have a distinct !dbg attachment +; CHECK: warning: ignoring invalid debug info +!9 = !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !6, file: !6, line: 2, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !7) +!11 = !DISubroutineType(types: !7) From 9b4eb3244fa4304249d53bed48ace6d701d6bd5f Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Tue, 8 Sep 2020 14:45:41 -0700 Subject: [PATCH 07/25] [profile] Add %t LLVM_PROFILE_FILE option to substitute $TMPDIR Add support for expanding the %t filename specifier in LLVM_PROFILE_FILE to the TMPDIR environment variable. This is supported on all platforms. On Darwin, TMPDIR is used to specify a temporary application-specific scratch directory. When testing apps on remote devices, it can be challenging for the host device to determine the correct TMPDIR, so it's helpful to have the runtime do this work. rdar://68524185 Differential Revision: https://reviews.llvm.org/D87332 (cherry picked from commit 62c372770d2e87f3e882a20d43c6814e6c4fe0f5) --- clang/docs/SourceBasedCodeCoverage.rst | 3 +++ compiler-rt/lib/profile/InstrProfilingFile.c | 28 +++++++++++++++----- compiler-rt/test/profile/instrprof-tmpdir.c | 22 +++++++++++++++ 3 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 compiler-rt/test/profile/instrprof-tmpdir.c diff --git a/clang/docs/SourceBasedCodeCoverage.rst b/clang/docs/SourceBasedCodeCoverage.rst index 0e9c364fbf6bd..f5b3d05262e93 100644 --- a/clang/docs/SourceBasedCodeCoverage.rst +++ b/clang/docs/SourceBasedCodeCoverage.rst @@ -79,6 +79,9 @@ directory structure will be created. Additionally, the following special * "%h" expands out to the hostname of the machine running the program. +* "%t" expands out to the value of the ``TMPDIR`` environment variable. On + Darwin, this is typically set to a temporary scratch directory. + * "%Nm" expands out to the instrumented binary's signature. When this pattern is specified, the runtime creates a pool of N raw profiles which are used for on-line profile merging. The runtime takes care of selecting a raw profile diff --git a/compiler-rt/lib/profile/InstrProfilingFile.c b/compiler-rt/lib/profile/InstrProfilingFile.c index 311eb877cdc49..13878f20e1014 100644 --- a/compiler-rt/lib/profile/InstrProfilingFile.c +++ b/compiler-rt/lib/profile/InstrProfilingFile.c @@ -74,6 +74,7 @@ typedef struct lprofFilename { unsigned OwnsFilenamePat; const char *ProfilePathPrefix; char PidChars[MAX_PID_SIZE]; + char *TmpDir; char Hostname[COMPILER_RT_MAX_HOSTLEN]; unsigned NumPids; unsigned NumHosts; @@ -90,8 +91,8 @@ typedef struct lprofFilename { ProfileNameSpecifier PNS; } lprofFilename; -static lprofFilename lprofCurFilename = {0, 0, 0, {0}, {0}, 0, - 0, 0, {0}, 0, PNS_unknown}; +static lprofFilename lprofCurFilename = {0, 0, 0, {0}, NULL, {0}, + 0, 0, 0, {0}, 0, PNS_unknown}; static int ProfileMergeRequested = 0; static int isProfileMergeRequested() { return ProfileMergeRequested; } @@ -773,6 +774,14 @@ static int parseFilenamePattern(const char *FilenamePat, FilenamePat); return -1; } + } else if (FilenamePat[I] == 't') { + lprofCurFilename.TmpDir = getenv("TMPDIR"); + if (!lprofCurFilename.TmpDir) { + PROF_WARN("Unable to get the TMPDIR environment variable, referenced " + "in %s. Using the default path.", + FilenamePat); + return -1; + } } else if (FilenamePat[I] == 'c') { if (__llvm_profile_is_continuous_mode_enabled()) { PROF_WARN("%%c specifier can only be specified once in %s.\n", @@ -874,12 +883,14 @@ static int getCurFilenameLength() { return 0; if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts || - lprofCurFilename.MergePoolSize || lprofCurFilename.NumExitSignals)) + lprofCurFilename.TmpDir || lprofCurFilename.MergePoolSize || + lprofCurFilename.NumExitSignals)) return strlen(lprofCurFilename.FilenamePat); Len = strlen(lprofCurFilename.FilenamePat) + lprofCurFilename.NumPids * (strlen(lprofCurFilename.PidChars) - 2) + - lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2); + lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2) + + (lprofCurFilename.TmpDir ? (strlen(lprofCurFilename.TmpDir) - 1) : 0); if (lprofCurFilename.MergePoolSize) Len += SIGLEN; for (I = 0; I < lprofCurFilename.NumExitSignals; ++I) { @@ -896,14 +907,14 @@ static int getCurFilenameLength() { * current filename pattern string is directly returned, unless ForceUseBuf * is enabled. */ static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf) { - int I, J, PidLength, HostNameLength, FilenamePatLength; + int I, J, PidLength, HostNameLength, TmpDirLength, FilenamePatLength; const char *FilenamePat = lprofCurFilename.FilenamePat; if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0]) return 0; if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts || - lprofCurFilename.MergePoolSize || + lprofCurFilename.TmpDir || lprofCurFilename.MergePoolSize || __llvm_profile_is_continuous_mode_enabled() || lprofCurFilename.NumExitSignals)) { if (!ForceUseBuf) @@ -917,6 +928,7 @@ static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf) { PidLength = strlen(lprofCurFilename.PidChars); HostNameLength = strlen(lprofCurFilename.Hostname); + TmpDirLength = lprofCurFilename.TmpDir ? strlen(lprofCurFilename.TmpDir) : 0; /* Construct the new filename. */ for (I = 0, J = 0; FilenamePat[I]; ++I) if (FilenamePat[I] == '%') { @@ -929,6 +941,10 @@ static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf) { } else if (containsExitOnSignalSpecifier(FilenamePat, I)) { while (FilenamePat[I] != 'x') ++I; + } else if (FilenamePat[I] == 't') { + memcpy(FilenameBuf + J, lprofCurFilename.TmpDir, TmpDirLength); + FilenameBuf[J + TmpDirLength] = DIR_SEPARATOR; + J += TmpDirLength + 1; } else { if (!getMergePoolSize(FilenamePat, &I)) continue; diff --git a/compiler-rt/test/profile/instrprof-tmpdir.c b/compiler-rt/test/profile/instrprof-tmpdir.c new file mode 100644 index 0000000000000..036313850ed63 --- /dev/null +++ b/compiler-rt/test/profile/instrprof-tmpdir.c @@ -0,0 +1,22 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: cd %t +// RUN: %clang_profgen -o %t/binary %s +// +// Check that a dir separator is appended after %t is subsituted. +// RUN: env TMPDIR="%t" LLVM_PROFILE_FILE="%%traw1.profraw" %run ./binary +// RUN: llvm-profdata show ./raw1.profraw | FileCheck %s -check-prefix TMPDIR +// +// Check that substitution works even if a redundant dir separator is added. +// RUN: env TMPDIR="%t" LLVM_PROFILE_FILE="%%t/raw2.profraw" %run ./binary +// RUN: llvm-profdata show ./raw2.profraw | FileCheck %s -check-prefix TMPDIR +// +// Check that we fall back to the default path if TMPDIR is missing. +// RUN: env -u TMPDIR LLVM_PROFILE_FILE="%%t/raw3.profraw" %run ./binary 2>&1 | FileCheck %s -check-prefix MISSING +// RUN: llvm-profdata show ./default.profraw | FileCheck %s -check-prefix TMPDIR + +// TMPDIR: Maximum function count: 1 + +// MISSING: Unable to get the TMPDIR environment variable, referenced in {{.*}}raw3.profraw. Using the default path. + +int main() { return 0; } From fdfe9903f5a6f6e8bf25659f918d840eeb3a1f2a Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Fri, 25 Sep 2020 10:26:35 -0700 Subject: [PATCH 08/25] Fix Assembler/disubprogram.ll after e17f52d623cc146b7d9bf5a2e02965043508b4c4 (cherry picked from commit 6caf3fb8178699ac14fb94fef99aaf1cf297264f) --- llvm/test/Assembler/disubprogram.ll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/test/Assembler/disubprogram.ll b/llvm/test/Assembler/disubprogram.ll index 10bd5520d84dc..6feb27ce48012 100644 --- a/llvm/test/Assembler/disubprogram.ll +++ b/llvm/test/Assembler/disubprogram.ll @@ -25,8 +25,8 @@ define void @_Z3foov() !dbg !9 { isOptimized: true, flags: "-O2", splitDebugFilename: "abc.debug", emissionKind: 2) -; CHECK: !9 = !DISubprogram(scope: null, spFlags: 0) -!9 = !DISubprogram(isDefinition: false) +; CHECK: !9 = distinct !DISubprogram(scope: null, spFlags: 0) +!9 = distinct !DISubprogram(isDefinition: false) ; CHECK: !10 = distinct !DISubprogram(name: "foo", linkageName: "_Zfoov", scope: !1, file: !2, line: 7, type: !3, scopeLine: 8, containingType: !4, virtualIndex: 10, thisAdjustment: 3, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: DISPFlagPureVirtual | DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !8, templateParams: !5, declaration: !9, retainedNodes: !6) !10 = distinct !DISubprogram(name: "foo", linkageName: "_Zfoov", scope: !1, From be39e9d01f6085b75ee343f781607da4a24d3024 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Fri, 25 Sep 2020 10:51:54 -0700 Subject: [PATCH 09/25] Revert "Add a verifier check that rejects non-distinct DISubprogram function" This reverts commit e17f52d623cc146b7d9bf5a2e02965043508b4c4. while investigating bot breakage. (cherry picked from commit 8055ae31f46b0a3fafd7b64f6cd77b78b34e6753) --- llvm/lib/IR/Verifier.cpp | 4 ---- .../Generic/2009-11-03-InsertExtractValue.ll | 2 +- llvm/test/Verifier/unique-disubprogram.ll | 16 ---------------- 3 files changed, 1 insertion(+), 21 deletions(-) delete mode 100644 llvm/test/Verifier/unique-disubprogram.ll diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 2e9c2e4af90df..f81e947dc94c2 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -2404,10 +2404,6 @@ void Verifier::visitFunction(const Function &F) { "function must have a single !dbg attachment", &F, I.second); AssertDI(isa(I.second), "function !dbg attachment must be a subprogram", &F, I.second); - AssertDI(cast(I.second)->isDistinct(), - "function definition may only have a distinct !dbg attachment", - &F); - auto *SP = cast(I.second); const Function *&AttachedTo = DISubprogramAttachments[SP]; AssertDI(!AttachedTo || AttachedTo == &F, diff --git a/llvm/test/DebugInfo/Generic/2009-11-03-InsertExtractValue.ll b/llvm/test/DebugInfo/Generic/2009-11-03-InsertExtractValue.ll index 81bb5f2457ed9..57ee7ebbb2cb3 100644 --- a/llvm/test/DebugInfo/Generic/2009-11-03-InsertExtractValue.ll +++ b/llvm/test/DebugInfo/Generic/2009-11-03-InsertExtractValue.ll @@ -3,7 +3,7 @@ !llvm.module.flags = !{!6} !llvm.dbg.cu = !{!5} -!0 = distinct !DISubprogram(name: "bar", linkageName: "_ZN3foo3barEv", line: 3, isLocal: false, isDefinition: false, virtualIndex: 6, flags: DIFlagProtected | DIFlagPrototyped, isOptimized: false, scopeLine: 3, file: !4, scope: !1, type: !2) +!0 = !DISubprogram(name: "bar", linkageName: "_ZN3foo3barEv", line: 3, isLocal: false, isDefinition: false, virtualIndex: 6, flags: DIFlagProtected | DIFlagPrototyped, isOptimized: false, scopeLine: 3, file: !4, scope: !1, type: !2) !1 = !DIFile(filename: "/foo", directory: "bar.cpp") !2 = !DISubroutineType(types: !3) !3 = !{null} diff --git a/llvm/test/Verifier/unique-disubprogram.ll b/llvm/test/Verifier/unique-disubprogram.ll deleted file mode 100644 index b4789a9293f7b..0000000000000 --- a/llvm/test/Verifier/unique-disubprogram.ll +++ /dev/null @@ -1,16 +0,0 @@ -; RUN: llvm-as -disable-output <%s 2>&1| FileCheck %s -define i32 @_Z3foov() local_unnamed_addr !dbg !9 { - ret i32 5 -} -!llvm.module.flags = !{!2} -!llvm.dbg.cu = !{!5} -!llvm.linker.options = !{} - -!2 = !{i32 2, !"Debug Info Version", i32 3} -!5 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !6, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug) -!6 = !DIFile(filename: "t.cpp", directory: "/") -!7 = !{} -; CHECK: function definition may only have a distinct !dbg attachment -; CHECK: warning: ignoring invalid debug info -!9 = !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !6, file: !6, line: 2, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !7) -!11 = !DISubroutineType(types: !7) From 52f9998ca9c405e555cdd437b2b40a16a6881722 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Thu, 24 Sep 2020 16:59:36 -0700 Subject: [PATCH 10/25] Add a verifier check that rejects non-distinct DISubprogram function attachments. They would crash the backend, which expects all DISubprograms that are not part of the type system to have a unit field. Clang right before https://reviews.llvm.org/D79967 would generate this kind of broken IR. rdar://problem/69534688 Thanks to Fangrui for fixing an assembler test I had missed! https://reviews.llvm.org/D88270 (cherry picked from commit 137597d4f47854bb1701f6883d5c91e8a14d29a2) --- llvm/lib/IR/Verifier.cpp | 4 ++++ .../Generic/2009-11-03-InsertExtractValue.ll | 2 +- llvm/test/Verifier/unique-disubprogram.ll | 16 ++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 llvm/test/Verifier/unique-disubprogram.ll diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index f81e947dc94c2..2e9c2e4af90df 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -2404,6 +2404,10 @@ void Verifier::visitFunction(const Function &F) { "function must have a single !dbg attachment", &F, I.second); AssertDI(isa(I.second), "function !dbg attachment must be a subprogram", &F, I.second); + AssertDI(cast(I.second)->isDistinct(), + "function definition may only have a distinct !dbg attachment", + &F); + auto *SP = cast(I.second); const Function *&AttachedTo = DISubprogramAttachments[SP]; AssertDI(!AttachedTo || AttachedTo == &F, diff --git a/llvm/test/DebugInfo/Generic/2009-11-03-InsertExtractValue.ll b/llvm/test/DebugInfo/Generic/2009-11-03-InsertExtractValue.ll index 57ee7ebbb2cb3..81bb5f2457ed9 100644 --- a/llvm/test/DebugInfo/Generic/2009-11-03-InsertExtractValue.ll +++ b/llvm/test/DebugInfo/Generic/2009-11-03-InsertExtractValue.ll @@ -3,7 +3,7 @@ !llvm.module.flags = !{!6} !llvm.dbg.cu = !{!5} -!0 = !DISubprogram(name: "bar", linkageName: "_ZN3foo3barEv", line: 3, isLocal: false, isDefinition: false, virtualIndex: 6, flags: DIFlagProtected | DIFlagPrototyped, isOptimized: false, scopeLine: 3, file: !4, scope: !1, type: !2) +!0 = distinct !DISubprogram(name: "bar", linkageName: "_ZN3foo3barEv", line: 3, isLocal: false, isDefinition: false, virtualIndex: 6, flags: DIFlagProtected | DIFlagPrototyped, isOptimized: false, scopeLine: 3, file: !4, scope: !1, type: !2) !1 = !DIFile(filename: "/foo", directory: "bar.cpp") !2 = !DISubroutineType(types: !3) !3 = !{null} diff --git a/llvm/test/Verifier/unique-disubprogram.ll b/llvm/test/Verifier/unique-disubprogram.ll new file mode 100644 index 0000000000000..b4789a9293f7b --- /dev/null +++ b/llvm/test/Verifier/unique-disubprogram.ll @@ -0,0 +1,16 @@ +; RUN: llvm-as -disable-output <%s 2>&1| FileCheck %s +define i32 @_Z3foov() local_unnamed_addr !dbg !9 { + ret i32 5 +} +!llvm.module.flags = !{!2} +!llvm.dbg.cu = !{!5} +!llvm.linker.options = !{} + +!2 = !{i32 2, !"Debug Info Version", i32 3} +!5 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !6, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug) +!6 = !DIFile(filename: "t.cpp", directory: "/") +!7 = !{} +; CHECK: function definition may only have a distinct !dbg attachment +; CHECK: warning: ignoring invalid debug info +!9 = !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !6, file: !6, line: 2, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !7) +!11 = !DISubroutineType(types: !7) From ecf89c52d9c37bfe01554ec7b3f71050f00c85bd Mon Sep 17 00:00:00 2001 From: shafik Date: Thu, 24 Sep 2020 14:31:31 -0700 Subject: [PATCH 11/25] [LLDB] Add a defensive check for member__f_ I only have a crash log and was not able to come up with a test case for this. rdar://problem/69403150 (cherry picked from commit a079f619b5a1959af8af37cabdea27ae542903db) --- .../Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp index 8aa803a8553e9..d3a25f37985f1 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp @@ -154,6 +154,9 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( member__f_ = sub_member__f_; } + if (!member__f_) + return optional_info; + lldb::addr_t member__f_pointer_value = member__f_->GetValueAsUnsigned(0); optional_info.member__f_pointer_value = member__f_pointer_value; From 8a75d2b5651b0056ad4fc9ade5546cd59c602e62 Mon Sep 17 00:00:00 2001 From: Zixu Wang Date: Tue, 16 Jun 2020 16:39:50 -0700 Subject: [PATCH 12/25] [clang][module] Improve incomplete-umbrella warning Change the warning message for -Wincomplete-umbrella to report the location of the umbrella header; Differential Revision: https://reviews.llvm.org/D82118 (cherry picked from commit ed79827aea444e6995fb3d36abc2bfd36331773c) --- clang/lib/Lex/PPLexerChange.cpp | 12 +++++++----- clang/test/Modules/incomplete-umbrella.m | 8 ++++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp index b7c7e2693ef18..de7b9b73ddf7a 100644 --- a/clang/lib/Lex/PPLexerChange.cpp +++ b/clang/lib/Lex/PPLexerChange.cpp @@ -263,10 +263,12 @@ static void collectAllSubModulesWithUmbrellaHeader( } void Preprocessor::diagnoseMissingHeaderInUmbrellaDir(const Module &Mod) { - assert(Mod.getUmbrellaHeader() && "Module must use umbrella header"); - SourceLocation StartLoc = - SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID()); - if (getDiagnostics().isIgnored(diag::warn_uncovered_module_header, StartLoc)) + const Module::Header &UmbrellaHeader = Mod.getUmbrellaHeader(); + assert(UmbrellaHeader.Entry && "Module must use umbrella header"); + const FileID &File = SourceMgr.translateFile(UmbrellaHeader.Entry); + SourceLocation ExpectedHeadersLoc = SourceMgr.getLocForEndOfFile(File); + if (getDiagnostics().isIgnored(diag::warn_uncovered_module_header, + ExpectedHeadersLoc)) return; ModuleMap &ModMap = getHeaderSearchInfo().getModuleMap(); @@ -291,7 +293,7 @@ void Preprocessor::diagnoseMissingHeaderInUmbrellaDir(const Module &Mod) { // Find the relative path that would access this header. SmallString<128> RelativePath; computeRelativePath(FileMgr, Dir, *Header, RelativePath); - Diag(StartLoc, diag::warn_uncovered_module_header) + Diag(ExpectedHeadersLoc, diag::warn_uncovered_module_header) << Mod.getFullModuleName() << RelativePath; } } diff --git a/clang/test/Modules/incomplete-umbrella.m b/clang/test/Modules/incomplete-umbrella.m index 8760b815718b2..0574921203b2d 100644 --- a/clang/test/Modules/incomplete-umbrella.m +++ b/clang/test/Modules/incomplete-umbrella.m @@ -6,8 +6,12 @@ #import @import Foo.Private; -// CHECK: warning: umbrella header for module 'Foo' does not include header 'Bar.h' -// CHECK: warning: umbrella header for module 'Foo.Private' does not include header 'Baz.h' +// CHECK: While building module 'Foo' imported from {{.*[/\]}}incomplete-umbrella.m:4: +// CHECK-NEXT: In file included from :1: +// CHECK-NEXT: {{.*Foo[.]framework[/\]Headers[/\]}}FooPublic.h:2:1: warning: umbrella header for module 'Foo' does not include header 'Bar.h' +// CHECK: While building module 'Foo' imported from {{.*[/\]}}incomplete-umbrella.m:4: +// CHECK-NEXT: In file included from :2: +// CHECK-NEXT: {{.*Foo[.]framework[/\]PrivateHeaders[/\]}}Foo.h:2:1: warning: umbrella header for module 'Foo.Private' does not include header 'Baz.h' int foo() { int a = BAR_PUBLIC; int b = BAZ_PRIVATE; From 8fd613ec6166964bc0bfa2beb6e136f933af4d01 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Sun, 27 Sep 2020 01:15:00 -0700 Subject: [PATCH 13/25] Add support for firmware/standalone LC_NOTE "main bin spec" corefiles (#1864) When a Mach-O corefile has an LC_NOTE "main bin spec" for a standalone binary / firmware, with only a UUID and no load address, try to locate the binary and dSYM by UUID and if found, load it at offset 0 for the user. Add a test case that tests a firmware/standalone corefile with both the "kern ver str" and "main bin spec" LC_NOTEs. Differential Revision: https://reviews.llvm.org/D88282 (cherry picked from commit 1bec6eb3f5cba594698bae5b2789744e0c8ee5f2) --- lldb/include/lldb/Symbol/ObjectFile.h | 24 +- .../ObjectFile/Mach-O/ObjectFileMachO.cpp | 36 +- .../ObjectFile/Mach-O/ObjectFileMachO.h | 4 +- .../Process/mach-core/ProcessMachCore.cpp | 181 +++++---- .../macosx/lc-note/firmware-corefile/Makefile | 14 + .../TestFirmwareCorefiles.py | 133 +++++++ .../macosx/lc-note/firmware-corefile/bout.mk | 10 + .../create-empty-corefile.cpp | 347 ++++++++++++++++++ .../macosx/lc-note/firmware-corefile/main.c | 2 + 9 files changed, 660 insertions(+), 91 deletions(-) create mode 100644 lldb/test/API/macosx/lc-note/firmware-corefile/Makefile create mode 100644 lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py create mode 100644 lldb/test/API/macosx/lc-note/firmware-corefile/bout.mk create mode 100644 lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp create mode 100644 lldb/test/API/macosx/lc-note/firmware-corefile/main.c diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h index e814015c0bf7b..080724cb86bdd 100644 --- a/lldb/include/lldb/Symbol/ObjectFile.h +++ b/lldb/include/lldb/Symbol/ObjectFile.h @@ -91,6 +91,17 @@ class ObjectFile : public std::enable_shared_from_this, eStrataJIT }; + /// If we have a corefile binary hint, this enum + /// specifies the binary type which we can use to + /// select the correct DynamicLoader plugin. + enum BinaryType { + eBinaryTypeInvalid = 0, + eBinaryTypeUnknown, + eBinaryTypeKernel, /// kernel binary + eBinaryTypeUser, /// user process binary + eBinaryTypeStandalone /// standalone binary / firmware + }; + struct LoadableData { lldb::addr_t Dest; llvm::ArrayRef Contents; @@ -500,12 +511,17 @@ class ObjectFile : public std::enable_shared_from_this, /// If the uuid of the binary is specified, this will be set. /// If no UUID is available, will be cleared. /// + /// \param[out] type + /// Return the type of the binary, which will dictate which + /// DynamicLoader plugin should be used. + /// /// \return /// Returns true if either address or uuid has been set. - virtual bool GetCorefileMainBinaryInfo (lldb::addr_t &address, UUID &uuid) { - address = LLDB_INVALID_ADDRESS; - uuid.Clear(); - return false; + virtual bool GetCorefileMainBinaryInfo(lldb::addr_t &address, UUID &uuid, + ObjectFile::BinaryType &type) { + address = LLDB_INVALID_ADDRESS; + uuid.Clear(); + return false; } virtual lldb::RegisterContextSP diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index cb1645f697c6a..9f1d1d576814d 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -5519,7 +5519,8 @@ std::string ObjectFileMachO::GetIdentifierString() { return result; } -bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &address, UUID &uuid) { +bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &address, UUID &uuid, + ObjectFile::BinaryType &type) { address = LLDB_INVALID_ADDRESS; uuid.Clear(); ModuleSP module_sp(GetModule()); @@ -5542,24 +5543,43 @@ bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &address, UUID &uuid) { // "main bin spec" (main binary specification) data payload is // formatted: // uint32_t version [currently 1] - // uint32_t type [0 == unspecified, 1 == kernel, 2 == user - // process] uint64_t address [ UINT64_MAX if address not - // specified ] uuid_t uuid [ all zero's if uuid not - // specified ] uint32_t log2_pagesize [ process page size in log base - // 2, e.g. 4k pages are 12. 0 for unspecified ] + // uint32_t type [0 == unspecified, 1 == kernel, + // 2 == user process, 3 == firmware ] + // uint64_t address [ UINT64_MAX if address not specified ] + // uuid_t uuid [ all zero's if uuid not specified ] + // uint32_t log2_pagesize [ process page size in log base + // 2, e.g. 4k pages are 12. + // 0 for unspecified ] + // uint32_t unused [ for alignment ] if (strcmp("main bin spec", data_owner) == 0 && size >= 32) { offset = fileoff; uint32_t version; if (m_data.GetU32(&offset, &version, 1) != nullptr && version == 1) { - uint32_t type = 0; + uint32_t binspec_type = 0; uuid_t raw_uuid; memset(raw_uuid, 0, sizeof(uuid_t)); - if (m_data.GetU32(&offset, &type, 1) && + if (m_data.GetU32(&offset, &binspec_type, 1) && m_data.GetU64(&offset, &address, 1) && m_data.CopyData(offset, sizeof(uuid_t), raw_uuid) != 0) { uuid = UUID::fromOptionalData(raw_uuid, sizeof(uuid_t)); + // convert the "main bin spec" type into our + // ObjectFile::BinaryType enum + switch (binspec_type) { + case 0: + type = eBinaryTypeUnknown; + break; + case 1: + type = eBinaryTypeKernel; + break; + case 2: + type = eBinaryTypeUser; + break; + case 3: + type = eBinaryTypeStandalone; + break; + } return true; } } diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h index 0c1d178b19215..2308c496c4216 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h @@ -112,7 +112,9 @@ class ObjectFileMachO : public lldb_private::ObjectFile { std::string GetIdentifierString() override; - bool GetCorefileMainBinaryInfo (lldb::addr_t &address, lldb_private::UUID &uuid) override; + bool GetCorefileMainBinaryInfo(lldb::addr_t &address, + lldb_private::UUID &uuid, + ObjectFile::BinaryType &type) override; lldb::RegisterContextSP GetThreadContextAtIndex(uint32_t idx, lldb_private::Thread &thread) override; diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp index b78276d345e1f..bee161bde96b5 100644 --- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp +++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp @@ -281,8 +281,9 @@ Status ProcessMachCore::DoLoadCore() { addr_t objfile_binary_addr; UUID objfile_binary_uuid; - if (core_objfile->GetCorefileMainBinaryInfo (objfile_binary_addr, objfile_binary_uuid)) - { + ObjectFile::BinaryType type; + if (core_objfile->GetCorefileMainBinaryInfo(objfile_binary_addr, + objfile_binary_uuid, type)) { if (objfile_binary_addr != LLDB_INVALID_ADDRESS) { m_mach_kernel_addr = objfile_binary_addr; @@ -293,7 +294,7 @@ Status ProcessMachCore::DoLoadCore() { m_mach_kernel_addr); } } - + // This checks for the presence of an LC_IDENT string in a core file; // LC_IDENT is very obsolete and should not be used in new code, but if the // load command is present, let's use the contents. @@ -326,58 +327,80 @@ Status ProcessMachCore::DoLoadCore() { addr, corefile_identifier.c_str()); } } - if (found_main_binary_definitively == false - && corefile_identifier.find("EFI ") != std::string::npos) { - UUID uuid; + + // In the case where we have an LC_NOTE specifying a standalone + // binary with only a UUID (and no load address) (iBoot, EFI, etc), + // then let's try to force a load of the binary and set its + // load address to 0-offset. + // + // The two forms this can come in is either a + // 'kern ver str' LC_NOTE with "EFI UUID=...." + // 'main bin spec' LC_NOTE with UUID and no load address. + + if (found_main_binary_definitively == false && + (corefile_identifier.find("EFI ") != std::string::npos || + (objfile_binary_uuid.IsValid() && + objfile_binary_addr == LLDB_INVALID_ADDRESS))) { + UUID uuid; + if (objfile_binary_uuid.IsValid()) { + uuid = objfile_binary_uuid; + LLDB_LOGF(log, + "ProcessMachCore::DoLoadCore: Using the main bin spec " + "LC_NOTE with UUID %s and no load address", + uuid.GetAsString().c_str()); + } else { if (corefile_identifier.find("UUID=") != std::string::npos) { - size_t p = corefile_identifier.find("UUID=") + strlen("UUID="); - std::string uuid_str = corefile_identifier.substr(p, 36); - uuid.SetFromStringRef(uuid_str); + size_t p = corefile_identifier.find("UUID=") + strlen("UUID="); + std::string uuid_str = corefile_identifier.substr(p, 36); + uuid.SetFromStringRef(uuid_str); + if (uuid.IsValid()) { + LLDB_LOGF(log, + "ProcessMachCore::DoLoadCore: Using the EFI " + "from LC_IDENT/LC_NOTE 'kern ver str' string: '%s'", + corefile_identifier.c_str()); + } } - if (uuid.IsValid()) { - LLDB_LOGF(log, - "ProcessMachCore::DoLoadCore: Using the EFI " - "from LC_IDENT/LC_NOTE 'kern ver str' string: '%s'", - corefile_identifier.c_str()); - - // We're only given a UUID here, not a load address. - // But there are python scripts in the EFI binary's dSYM which - // know how to relocate the binary to the correct load address. - // lldb only needs to locate & load the binary + dSYM. - ModuleSpec module_spec; - module_spec.GetUUID() = uuid; - module_spec.GetArchitecture() = GetTarget().GetArchitecture(); - - // Lookup UUID locally, before attempting dsymForUUID like action - FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths(); - module_spec.GetSymbolFileSpec() = - Symbols::LocateExecutableSymbolFile(module_spec, search_paths); - if (module_spec.GetSymbolFileSpec()) { - ModuleSpec executable_module_spec = - Symbols::LocateExecutableObjectFile(module_spec); - if (FileSystem::Instance().Exists( - executable_module_spec.GetFileSpec())) { - module_spec.GetFileSpec() = executable_module_spec.GetFileSpec(); - } + } + + if (uuid.IsValid()) { + ModuleSpec module_spec; + module_spec.GetUUID() = uuid; + module_spec.GetArchitecture() = GetTarget().GetArchitecture(); + + // Lookup UUID locally, before attempting dsymForUUID-like action + FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths(); + module_spec.GetSymbolFileSpec() = + Symbols::LocateExecutableSymbolFile(module_spec, search_paths); + if (module_spec.GetSymbolFileSpec()) { + ModuleSpec executable_module_spec = + Symbols::LocateExecutableObjectFile(module_spec); + if (FileSystem::Instance().Exists( + executable_module_spec.GetFileSpec())) { + module_spec.GetFileSpec() = executable_module_spec.GetFileSpec(); } + } - // Force a a dsymForUUID lookup, if that tool is available. - if (!module_spec.GetSymbolFileSpec()) - Symbols::DownloadObjectAndSymbolFile(module_spec, true); - - if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) { - ModuleSP module_sp(new Module(module_spec)); - if (module_sp.get() && module_sp->GetObjectFile()) { - // Get the current target executable - ModuleSP exe_module_sp(GetTarget().GetExecutableModule()); - - // Make sure you don't already have the right module loaded - // and they will be uniqued - if (exe_module_sp.get() != module_sp.get()) - GetTarget().SetExecutableModule(module_sp, eLoadDependentsNo); - } + // Force a a dsymForUUID lookup, if that tool is available. + if (!module_spec.GetSymbolFileSpec()) + Symbols::DownloadObjectAndSymbolFile(module_spec, true); + + // If we found a binary, load it at offset 0 and set our + // dyld_plugin to be the static plugin. + if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) { + ModuleSP module_sp(new Module(module_spec)); + if (module_sp.get() && module_sp->GetObjectFile()) { + GetTarget().GetImages().AppendIfNeeded(module_sp, true); + GetTarget().SetExecutableModule(module_sp, eLoadDependentsNo); + found_main_binary_definitively = true; + bool changed = true; + module_sp->SetLoadAddress(GetTarget(), 0, true, changed); + ModuleList added_module; + added_module.Append(module_sp, false); + GetTarget().ModulesDidLoad(added_module); + m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); } } + } } if (!found_main_binary_definitively && @@ -440,35 +463,37 @@ Status ProcessMachCore::DoLoadCore() { } } - // If we found both a user-process dyld and a kernel binary, we need to - // decide which to prefer. - if (GetCorefilePreference() == eKernelCorefile) { - if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { - LLDB_LOGF(log, - "ProcessMachCore::DoLoadCore: Using kernel corefile image " - "at 0x%" PRIx64, - m_mach_kernel_addr); - m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); - } else if (m_dyld_addr != LLDB_INVALID_ADDRESS) { - LLDB_LOGF(log, - "ProcessMachCore::DoLoadCore: Using user process dyld " - "image at 0x%" PRIx64, - m_dyld_addr); - m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); - } - } else { - if (m_dyld_addr != LLDB_INVALID_ADDRESS) { - LLDB_LOGF(log, - "ProcessMachCore::DoLoadCore: Using user process dyld " - "image at 0x%" PRIx64, - m_dyld_addr); - m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); - } else if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { - LLDB_LOGF(log, - "ProcessMachCore::DoLoadCore: Using kernel corefile image " - "at 0x%" PRIx64, - m_mach_kernel_addr); - m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); + if (m_dyld_plugin_name.IsEmpty()) { + // If we found both a user-process dyld and a kernel binary, we need to + // decide which to prefer. + if (GetCorefilePreference() == eKernelCorefile) { + if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, + "ProcessMachCore::DoLoadCore: Using kernel corefile image " + "at 0x%" PRIx64, + m_mach_kernel_addr); + m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); + } else if (m_dyld_addr != LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, + "ProcessMachCore::DoLoadCore: Using user process dyld " + "image at 0x%" PRIx64, + m_dyld_addr); + m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); + } + } else { + if (m_dyld_addr != LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, + "ProcessMachCore::DoLoadCore: Using user process dyld " + "image at 0x%" PRIx64, + m_dyld_addr); + m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); + } else if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, + "ProcessMachCore::DoLoadCore: Using kernel corefile image " + "at 0x%" PRIx64, + m_mach_kernel_addr); + m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); + } } } diff --git a/lldb/test/API/macosx/lc-note/firmware-corefile/Makefile b/lldb/test/API/macosx/lc-note/firmware-corefile/Makefile new file mode 100644 index 0000000000000..faa5ef2f29f43 --- /dev/null +++ b/lldb/test/API/macosx/lc-note/firmware-corefile/Makefile @@ -0,0 +1,14 @@ +MAKE_DSYM := NO +C_SOURCES := main.c +CFLAGS_EXTRAS := -Wl,-random_uuid + +all: a.out b.out create-empty-corefile + +create-empty-corefile: + $(MAKE) -f $(MAKEFILE_RULES) EXE=create-empty-corefile \ + C_SOURCES=create-empty-corefile.c + +b.out: + $(MAKE) VPATH=$(SRCDIR)/$* -I $(SRCDIR) -f $(SRCDIR)/bout.mk + +include Makefile.rules diff --git a/lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py b/lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py new file mode 100644 index 0000000000000..79a79056476b7 --- /dev/null +++ b/lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py @@ -0,0 +1,133 @@ +"""Test that corefiles with LC_NOTE "kern ver str" and "main bin spec" load commands works.""" + + + +import os +import re +import subprocess + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestFirmwareCorefiles(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIf(debug_info=no_match(["dsym"]), bugnumber="This test is looking explicitly for a dSYM") + @skipIf(archs=no_match(['x86_64'])) + @skipUnlessDarwin + def test_lc_note(self): + self.build() + self.aout_exe = self.getBuildArtifact("a.out") + self.bout_exe = self.getBuildArtifact("b.out") + self.create_corefile = self.getBuildArtifact("create-empty-corefile") + self.dsym_for_uuid = self.getBuildArtifact("dsym-for-uuid.sh") + self.aout_corefile = self.getBuildArtifact("aout.core") + self.bout_corefile = self.getBuildArtifact("bout.core") + + ## We can hook in our dsym-for-uuid shell script to lldb with this env + ## var instead of requiring a defaults write. + os.environ['LLDB_APPLE_DSYMFORUUID_EXECUTABLE'] = self.dsym_for_uuid + self.addTearDownHook(lambda: os.environ.pop('LLDB_APPLE_DSYMFORUUID_EXECUTABLE', None)) + + dwarfdump_uuid_regex = re.compile( + 'UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*') + dwarfdump_cmd_output = subprocess.check_output( + ('/usr/bin/dwarfdump --uuid "%s"' % self.aout_exe), shell=True).decode("utf-8") + aout_uuid = None + for line in dwarfdump_cmd_output.splitlines(): + match = dwarfdump_uuid_regex.search(line) + if match: + aout_uuid = match.group(1) + self.assertNotEqual(aout_uuid, None, "Could not get uuid of built a.out") + + dwarfdump_cmd_output = subprocess.check_output( + ('/usr/bin/dwarfdump --uuid "%s"' % self.bout_exe), shell=True).decode("utf-8") + bout_uuid = None + for line in dwarfdump_cmd_output.splitlines(): + match = dwarfdump_uuid_regex.search(line) + if match: + bout_uuid = match.group(1) + self.assertNotEqual(bout_uuid, None, "Could not get uuid of built b.out") + + ### Create our dsym-for-uuid shell script which returns self.aout_exe + ### or self.bout_exe, depending on the UUID on the command line. + shell_cmds = [ + '#! /bin/sh', + '# the last argument is the uuid', + 'while [ $# -gt 1 ]', + 'do', + ' shift', + 'done', + 'ret=0', + 'echo ""', + 'echo ""', + 'echo ""', + '', + 'if [ "$1" != "%s" -a "$1" != "%s" ]' % (aout_uuid, bout_uuid), + 'then', + ' echo "DBGErrornot found"', + ' echo ""', + ' exit 1', + 'fi', + 'if [ "$1" = "%s" ]' % aout_uuid, + 'then', + ' uuid=%s' % aout_uuid, + ' bin=%s' % self.aout_exe, + ' dsym=%s.dSYM/Contents/Resources/DWARF/%s' % (self.aout_exe, os.path.basename(self.aout_exe)), + 'else', + ' uuid=%s' % bout_uuid, + ' bin=%s' % self.bout_exe, + ' dsym=%s.dSYM/Contents/Resources/DWARF/%s' % (self.bout_exe, os.path.basename(self.bout_exe)), + 'fi', + 'echo "$uuid"', + '', + 'echo "DBGArchitecturex86_64"', + 'echo "DBGDSYMPath$dsym"', + 'echo "DBGSymbolRichExecutable$bin"', + 'echo ""', + 'exit $ret' + ] + + with open(self.dsym_for_uuid, "w") as writer: + for l in shell_cmds: + writer.write(l + '\n') + + os.chmod(self.dsym_for_uuid, 0o755) + + ### Create our corefile + retcode = call(self.create_corefile + " version-string " + self.aout_corefile + " " + self.aout_exe, shell=True) + retcode = call(self.create_corefile + " main-bin-spec " + self.bout_corefile + " " + self.bout_exe, shell=True) + + ### Now run lldb on the corefile + ### which will give us a UUID + ### which we call dsym-for-uuid.sh with + ### which gives us a binary and dSYM + ### which lldb should load! + + # First, try the "kern ver str" corefile + self.target = self.dbg.CreateTarget('') + err = lldb.SBError() + self.process = self.target.LoadCore(self.aout_corefile) + self.assertEqual(self.process.IsValid(), True) + if self.TraceOn(): + self.runCmd("image list") + self.assertEqual(self.target.GetNumModules(), 1) + fspec = self.target.GetModuleAtIndex(0).GetFileSpec() + filepath = fspec.GetDirectory() + "/" + fspec.GetFilename() + self.assertEqual(filepath, self.aout_exe) + + + # Second, try the "main bin spec" corefile + self.target = self.dbg.CreateTarget('') + self.process = self.target.LoadCore(self.bout_corefile) + self.assertEqual(self.process.IsValid(), True) + if self.TraceOn(): + self.runCmd("image list") + self.assertEqual(self.target.GetNumModules(), 1) + fspec = self.target.GetModuleAtIndex(0).GetFileSpec() + filepath = fspec.GetDirectory() + "/" + fspec.GetFilename() + self.assertEqual(filepath, self.bout_exe) diff --git a/lldb/test/API/macosx/lc-note/firmware-corefile/bout.mk b/lldb/test/API/macosx/lc-note/firmware-corefile/bout.mk new file mode 100644 index 0000000000000..049b7b49aa461 --- /dev/null +++ b/lldb/test/API/macosx/lc-note/firmware-corefile/bout.mk @@ -0,0 +1,10 @@ +MAKE_DSYM := NO + +C_SOURCES := main.c +CFLAGS_EXTRAS := -Wl,-random_uuid + +EXE := b.out + +all: b.out + +include Makefile.rules diff --git a/lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp b/lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp new file mode 100644 index 0000000000000..5e02bad51d765 --- /dev/null +++ b/lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp @@ -0,0 +1,347 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// Create an empty corefile with a "kern ver str" LC_NOTE +// or a "main bin spec" LC_NOTE.. +// If an existing binary is given as a 3rd argument on the cmd line, +// the UUID from that binary will be encoded in the corefile. +// Otherwise a pre-set UUID will be put in the corefile that +// is created. + +struct main_bin_spec_payload { + uint32_t version; + uint32_t type; + uint64_t address; + uuid_t uuid; + uint32_t log2_pagesize; + uint32_t unused; +}; + +union uint32_buf { + uint8_t bytebuf[4]; + uint32_t val; +}; + +union uint64_buf { + uint8_t bytebuf[8]; + uint64_t val; +}; + +void add_uint64(std::vector &buf, uint64_t val) { + uint64_buf conv; + conv.val = val; + for (int i = 0; i < 8; i++) + buf.push_back(conv.bytebuf[i]); +} + +void add_uint32(std::vector &buf, uint32_t val) { + uint32_buf conv; + conv.val = val; + for (int i = 0; i < 4; i++) + buf.push_back(conv.bytebuf[i]); +} + +std::vector x86_lc_thread_load_command() { + std::vector data; + add_uint32(data, LC_THREAD); // thread_command.cmd + add_uint32(data, 184); // thread_command.cmdsize + add_uint32(data, x86_THREAD_STATE64); // thread_command.flavor + add_uint32(data, x86_THREAD_STATE64_COUNT); // thread_command.count + add_uint64(data, 0x0000000000000000); // rax + add_uint64(data, 0x0000000000000400); // rbx + add_uint64(data, 0x0000000000000000); // rcx + add_uint64(data, 0x0000000000000000); // rdx + add_uint64(data, 0x0000000000000000); // rdi + add_uint64(data, 0x0000000000000000); // rsi + add_uint64(data, 0xffffff9246e2ba20); // rbp + add_uint64(data, 0xffffff9246e2ba10); // rsp + add_uint64(data, 0x0000000000000000); // r8 + add_uint64(data, 0x0000000000000000); // r9 + add_uint64(data, 0x0000000000000000); // r10 + add_uint64(data, 0x0000000000000000); // r11 + add_uint64(data, 0xffffff7f96ce5fe1); // r12 + add_uint64(data, 0x0000000000000000); // r13 + add_uint64(data, 0x0000000000000000); // r14 + add_uint64(data, 0xffffff9246e2bac0); // r15 + add_uint64(data, 0xffffff8015a8f6d0); // rip + add_uint64(data, 0x0000000000011111); // rflags + add_uint64(data, 0x0000000000022222); // cs + add_uint64(data, 0x0000000000033333); // fs + add_uint64(data, 0x0000000000044444); // gs + return data; +} + +void add_lc_note_kern_ver_str_load_command( + std::vector> &loadcmds, std::vector &payload, + int payload_file_offset, std::string uuid) { + std::string ident = "EFI UUID="; + ident += uuid; + std::vector loadcmd_data; + + add_uint32(loadcmd_data, LC_NOTE); // note_command.cmd + add_uint32(loadcmd_data, 40); // note_command.cmdsize + char lc_note_name[16]; + memset(lc_note_name, 0, 16); + strcpy(lc_note_name, "kern ver str"); + + // lc_note.data_owner + for (int i = 0; i < 16; i++) + loadcmd_data.push_back(lc_note_name[i]); + + // we start writing the payload at payload_file_offset to leave + // room at the start for the header & the load commands. + uint64_t current_payload_offset = payload.size() + payload_file_offset; + + add_uint64(loadcmd_data, current_payload_offset); // note_command.offset + add_uint64(loadcmd_data, 4 + ident.size() + 1); // note_command.size + + loadcmds.push_back(loadcmd_data); + + add_uint32(payload, 1); // kerneL_version_string.version + for (int i = 0; i < ident.size() + 1; i++) { + payload.push_back(ident[i]); + } +} + +void add_lc_note_main_bin_spec_load_command( + std::vector> &loadcmds, std::vector &payload, + int payload_file_offset, std::string uuidstr) { + std::vector loadcmd_data; + + add_uint32(loadcmd_data, LC_NOTE); // note_command.cmd + add_uint32(loadcmd_data, 40); // note_command.cmdsize + char lc_note_name[16]; + memset(lc_note_name, 0, 16); + strcpy(lc_note_name, "main bin spec"); + + // lc_note.data_owner + for (int i = 0; i < 16; i++) + loadcmd_data.push_back(lc_note_name[i]); + + // we start writing the payload at payload_file_offset to leave + // room at the start for the header & the load commands. + uint64_t current_payload_offset = payload.size() + payload_file_offset; + + add_uint64(loadcmd_data, current_payload_offset); // note_command.offset + add_uint64(loadcmd_data, + sizeof(struct main_bin_spec_payload)); // note_command.size + + loadcmds.push_back(loadcmd_data); + + // Now write the "main bin spec" payload. + add_uint32(payload, 1); // version + add_uint32(payload, 3); // type == 3 [ firmware, standalone,e tc ] + add_uint64(payload, UINT64_MAX); // load address unknown/unspecified + uuid_t uuid; + uuid_parse(uuidstr.c_str(), uuid); + for (int i = 0; i < sizeof(uuid_t); i++) + payload.push_back(uuid[i]); + add_uint32(payload, 0); // log2_pagesize unspecified + add_uint32(payload, 0); // unused +} + +void add_lc_segment(std::vector> &loadcmds, + std::vector &payload, int payload_file_offset) { + std::vector loadcmd_data; + struct segment_command_64 seg; + seg.cmd = LC_SEGMENT_64; + seg.cmdsize = sizeof(struct segment_command_64); // no sections + memset(seg.segname, 0, 16); + seg.vmaddr = 0xffffff7f96400000; + seg.vmsize = 4096; + seg.fileoff = payload.size() + payload_file_offset; + seg.filesize = 0; + seg.maxprot = 1; + seg.initprot = 1; + seg.nsects = 0; + seg.flags = 0; + + uint8_t *p = (uint8_t *)&seg; + for (int i = 0; i < sizeof(struct segment_command_64); i++) { + loadcmd_data.push_back(*(p + i)); + } + loadcmds.push_back(loadcmd_data); +} + +std::string get_uuid_from_binary(const char *fn) { + FILE *f = fopen(fn, "r"); + if (f == nullptr) { + fprintf(stderr, "Unable to open binary '%s' to get uuid\n", fn); + exit(1); + } + uint32_t num_of_load_cmds = 0; + uint32_t size_of_load_cmds = 0; + std::string uuid; + off_t file_offset = 0; + + uint8_t magic[4]; + if (::fread(magic, 1, 4, f) != 4) { + fprintf(stderr, "Failed to read magic number from input file %s\n", fn); + exit(1); + } + uint8_t magic_32_be[] = {0xfe, 0xed, 0xfa, 0xce}; + uint8_t magic_32_le[] = {0xce, 0xfa, 0xed, 0xfe}; + uint8_t magic_64_be[] = {0xfe, 0xed, 0xfa, 0xcf}; + uint8_t magic_64_le[] = {0xcf, 0xfa, 0xed, 0xfe}; + + if (memcmp(magic, magic_32_be, 4) == 0 || + memcmp(magic, magic_64_be, 4) == 0) { + fprintf(stderr, "big endian corefiles not supported\n"); + exit(1); + } + + ::fseeko(f, 0, SEEK_SET); + if (memcmp(magic, magic_32_le, 4) == 0) { + struct mach_header mh; + if (::fread(&mh, 1, sizeof(mh), f) != sizeof(mh)) { + fprintf(stderr, "error reading mach header from input file\n"); + exit(1); + } + if (mh.cputype != CPU_TYPE_X86_64) { + fprintf(stderr, + "This tool creates an x86_64 corefile but " + "the supplied binary '%s' is cputype 0x%x\n", + fn, (uint32_t)mh.cputype); + exit(1); + } + num_of_load_cmds = mh.ncmds; + size_of_load_cmds = mh.sizeofcmds; + file_offset += sizeof(struct mach_header); + } else { + struct mach_header_64 mh; + if (::fread(&mh, 1, sizeof(mh), f) != sizeof(mh)) { + fprintf(stderr, "error reading mach header from input file\n"); + exit(1); + } + if (mh.cputype != CPU_TYPE_X86_64) { + fprintf(stderr, + "This tool creates an x86_64 corefile but " + "the supplied binary '%s' is cputype 0x%x\n", + fn, (uint32_t)mh.cputype); + exit(1); + } + num_of_load_cmds = mh.ncmds; + size_of_load_cmds = mh.sizeofcmds; + file_offset += sizeof(struct mach_header_64); + } + + off_t load_cmds_offset = file_offset; + + for (int i = 0; i < num_of_load_cmds && + (file_offset - load_cmds_offset) < size_of_load_cmds; + i++) { + ::fseeko(f, file_offset, SEEK_SET); + uint32_t cmd; + uint32_t cmdsize; + ::fread(&cmd, sizeof(uint32_t), 1, f); + ::fread(&cmdsize, sizeof(uint32_t), 1, f); + if (cmd == LC_UUID) { + struct uuid_command uuidcmd; + ::fseeko(f, file_offset, SEEK_SET); + if (::fread(&uuidcmd, 1, sizeof(uuidcmd), f) != sizeof(uuidcmd)) { + fprintf(stderr, "Unable to read LC_UUID load command.\n"); + exit(1); + } + uuid_string_t uuidstr; + uuid_unparse(uuidcmd.uuid, uuidstr); + uuid = uuidstr; + break; + } + file_offset += cmdsize; + } + return uuid; +} + +int main(int argc, char **argv) { + if (argc != 4) { + fprintf(stderr, "usage: create-empty-corefile version-string|main-bin-spec " + " \n"); + fprintf( + stderr, + "Create a Mach-O corefile with an either LC_NOTE 'kern ver str' or \n"); + fprintf(stderr, "an LC_NOTE 'main bin spec' load command without an " + "address specified, depending on\n"); + fprintf(stderr, "whether the 1st arg is version-string or main-bin-spec\n"); + exit(1); + } + if (strcmp(argv[1], "version-string") != 0 && + strcmp(argv[1], "main-bin-spec") != 0) { + fprintf(stderr, "arg1 was not version-string or main-bin-spec\n"); + exit(1); + } + + std::string uuid = get_uuid_from_binary(argv[3]); + + // An array of load commands (in the form of byte arrays) + std::vector> load_commands; + + // An array of corefile contents (page data, lc_note data, etc) + std::vector payload; + + // First add all the load commands / payload so we can figure out how large + // the load commands will actually be. + load_commands.push_back(x86_lc_thread_load_command()); + if (strcmp(argv[1], "version-string") == 0) + add_lc_note_kern_ver_str_load_command(load_commands, payload, 0, uuid); + else + add_lc_note_main_bin_spec_load_command(load_commands, payload, 0, uuid); + add_lc_segment(load_commands, payload, 0); + + int size_of_load_commands = 0; + for (const auto &lc : load_commands) + size_of_load_commands += lc.size(); + + int header_and_load_cmd_room = + sizeof(struct mach_header_64) + size_of_load_commands; + + // Erase the load commands / payload now that we know how much space is + // needed, redo it. + load_commands.clear(); + payload.clear(); + + load_commands.push_back(x86_lc_thread_load_command()); + + if (strcmp(argv[1], "version-string") == 0) + add_lc_note_kern_ver_str_load_command(load_commands, payload, + header_and_load_cmd_room, uuid); + else + add_lc_note_main_bin_spec_load_command(load_commands, payload, + header_and_load_cmd_room, uuid); + + add_lc_segment(load_commands, payload, header_and_load_cmd_room); + + struct mach_header_64 mh; + mh.magic = MH_MAGIC_64; + mh.cputype = CPU_TYPE_X86_64; + + mh.cpusubtype = CPU_SUBTYPE_X86_64_ALL; + mh.filetype = MH_CORE; + mh.ncmds = load_commands.size(); + mh.sizeofcmds = size_of_load_commands; + mh.flags = 0; + mh.reserved = 0; + + FILE *f = fopen(argv[2], "w"); + + if (f == nullptr) { + fprintf(stderr, "Unable to open file %s for writing\n", argv[2]); + exit(1); + } + + fwrite(&mh, sizeof(struct mach_header_64), 1, f); + + for (const auto &lc : load_commands) + fwrite(lc.data(), lc.size(), 1, f); + + fseek(f, header_and_load_cmd_room, SEEK_SET); + + fwrite(payload.data(), payload.size(), 1, f); + + fclose(f); +} diff --git a/lldb/test/API/macosx/lc-note/firmware-corefile/main.c b/lldb/test/API/macosx/lc-note/firmware-corefile/main.c new file mode 100644 index 0000000000000..70a72e0b80b1e --- /dev/null +++ b/lldb/test/API/macosx/lc-note/firmware-corefile/main.c @@ -0,0 +1,2 @@ +#include +int main () { puts ("this is the lc-note test program."); } From 184a13d362e041b1fcd14a5e782ba0b17d13dc3c Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Fri, 25 Sep 2020 10:26:36 -0400 Subject: [PATCH 14/25] AArch64/GlobalISel: Narrow stack passed argument access size This fixes a verifier error in the testcase from bug 47619. The stack passed s3 value was widened to 4-bytes, and producing a 4-byte memory access with a < 1 byte result type. We need to either widen the result type or narrow the access size. This copies the code directly from the AMDGPU handling, which narrows the load size. I don't like that every target has to handle this, but this is currently broken on the 11 release branch and this is the simplest fix. This reverts commit 42bfa7c63b85e76fe16521d1671afcafaf8f64ed. (cherry picked from commit 6cb0d23f2ea6fb25106b0380797ccbc2141d71e1) --- llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp | 9 +++++++-- .../GlobalISel/irtranslator-stack-evt-bug47619.ll | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp b/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp index 11a8d5def4296..4832ae8f415f2 100644 --- a/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp @@ -84,11 +84,16 @@ struct IncomingArgHandler : public CallLowering::ValueHandler { } } - void assignValueToAddress(Register ValVReg, Register Addr, uint64_t Size, + void assignValueToAddress(Register ValVReg, Register Addr, uint64_t MemSize, MachinePointerInfo &MPO, CCValAssign &VA) override { MachineFunction &MF = MIRBuilder.getMF(); + + // The reported memory location may be wider than the value. + const LLT RegTy = MRI.getType(ValVReg); + MemSize = std::min(static_cast(RegTy.getSizeInBytes()), MemSize); + auto MMO = MF.getMachineMemOperand( - MPO, MachineMemOperand::MOLoad | MachineMemOperand::MOInvariant, Size, + MPO, MachineMemOperand::MOLoad | MachineMemOperand::MOInvariant, MemSize, inferAlignFromPtrInfo(MF, MPO)); MIRBuilder.buildLoad(ValVReg, Addr, *MMO); } diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-stack-evt-bug47619.ll b/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-stack-evt-bug47619.ll index 552997e44f09c..ca36e5da5e5fb 100644 --- a/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-stack-evt-bug47619.ll +++ b/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-stack-evt-bug47619.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py -; RUN: llc -global-isel -mtriple=aarch64-unknown-unknown -stop-after=irtranslator %s -o - | FileCheck %s +; RUN: llc -global-isel -mtriple=aarch64-unknown-unknown -stop-after=irtranslator -verify-machineinstrs %s -o - | FileCheck %s ; Make sure the i3 %arg8 value is correctly handled. This was trying ; to use MVT for EVT values passed on the stack and asserting before @@ -17,7 +17,7 @@ define i3 @bug47619(i64 %arg, i64 %arg1, i64 %arg2, i64 %arg3, i64 %arg4, i64 %a ; CHECK: [[COPY6:%[0-9]+]]:_(s64) = COPY $x6 ; CHECK: [[COPY7:%[0-9]+]]:_(s64) = COPY $x7 ; CHECK: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %fixed-stack.0 - ; CHECK: [[LOAD:%[0-9]+]]:_(s3) = G_LOAD [[FRAME_INDEX]](p0) :: (invariant load 4 from %fixed-stack.0, align 16) + ; CHECK: [[LOAD:%[0-9]+]]:_(s3) = G_LOAD [[FRAME_INDEX]](p0) :: (invariant load 1 from %fixed-stack.0, align 16) ; CHECK: [[ANYEXT:%[0-9]+]]:_(s32) = G_ANYEXT [[LOAD]](s3) ; CHECK: $w0 = COPY [[ANYEXT]](s32) ; CHECK: RET_ReallyLR implicit $w0 From 1e4b179bf821bfff8fad7f46423494ed1f62dac0 Mon Sep 17 00:00:00 2001 From: Simon Atanasyan Date: Fri, 25 Sep 2020 00:01:07 +0300 Subject: [PATCH 15/25] [CodeGen] Do not call `emitGlobalConstantLargeInt` for constant requires 8 bytes to store This is a fix for PR47630. The regression is caused by the D78011. After this change the code starts to call the `emitGlobalConstantLargeInt` even for constants which requires eight bytes to store. Differential revision: https://reviews.llvm.org/D88261 (cherry picked from commit c6c5629f2fb4ddabd376fbe7c218733283e91d09) --- llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 2 +- llvm/test/CodeGen/Mips/emit-big-cst.ll | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index f8f7b74baf916..c7eb0257d71b8 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -2779,7 +2779,7 @@ static void emitGlobalConstantImpl(const DataLayout &DL, const Constant *CV, if (const ConstantInt *CI = dyn_cast(CV)) { const uint64_t StoreSize = DL.getTypeStoreSize(CV->getType()); - if (StoreSize < 8) { + if (StoreSize <= 8) { if (AP.isVerbose()) AP.OutStreamer->GetCommentOS() << format("0x%" PRIx64 "\n", CI->getZExtValue()); diff --git a/llvm/test/CodeGen/Mips/emit-big-cst.ll b/llvm/test/CodeGen/Mips/emit-big-cst.ll index 67c2f107db19a..679824ef047b6 100644 --- a/llvm/test/CodeGen/Mips/emit-big-cst.ll +++ b/llvm/test/CodeGen/Mips/emit-big-cst.ll @@ -16,6 +16,14 @@ ; LE-NEXT: .space 5 ; LE-NEXT: .size bigCst, 16 +; BE-LABEL: notSoBigCst: +; BE-NEXT: .8byte 72057594037927935 +; BE-NEXT: .size notSoBigCst, 8 + +; LE-LABEL: notSoBigCst: +; LE-NEXT: .8byte 72057594037927935 +; LE-NEXT: .size notSoBigCst, 8 + ; BE-LABEL: smallCst: ; BE-NEXT: .2byte 4386 ; BE-NEXT: .byte 51 @@ -38,4 +46,14 @@ define void @accessBig(i64* %storage) { ret void } +@notSoBigCst = internal constant i57 72057594037927935 + +define void @accessNotSoBig(i64* %storage) { + %addr = bitcast i64* %storage to i57* + %bigLoadedCst = load volatile i57, i57* @notSoBigCst + %tmp = add i57 %bigLoadedCst, 1 + store i57 %tmp, i57* %addr + ret void +} + @smallCst = internal constant i24 1122867 From 9e367bd69b0d2523237e204b43301e59a5badb29 Mon Sep 17 00:00:00 2001 From: Craig Disselkoen Date: Fri, 25 Sep 2020 14:34:23 -0700 Subject: [PATCH 16/25] C API: functions to get mask of a ShuffleVector This commit fixes a regression (from LLVM 10 to LLVM 11 RC3) in the LLVM C API. Previously, commit 1ee6ec2bf removed the mask operand from the ShuffleVector instruction, storing the mask data separately in the instruction instead; this reduced the number of operands of ShuffleVector from 3 to 2. AFAICT, this change unintentionally caused a regression in the LLVM C API. Specifically, it is no longer possible to get the mask of a ShuffleVector instruction through the C API. This patch introduces new functions which together allow a C API user to get the mask of a ShuffleVector instruction, restoring the functionality which was previously available through LLVMGetOperand(). This patch also adds tests for this change to the llvm-c-test executable, which involved adding support for InsertElement, ExtractElement, and ShuffleVector itself (as well as constant vectors) to echo.cpp. Previously, vector operations weren't tested at all in echo.ll. I also fixed some typos in comments and help-text nearby these changes, which I happened to spot while developing this patch. Since the typo fixes are technically unrelated other than being in the same files, I'm happy to take them out if you'd rather they not be included in the patch. Differential Revision: https://reviews.llvm.org/D88190 (cherry picked from commit 51cad041e0cb26597c7ccc0fbfaa349b8fffbcda) --- llvm/include/llvm-c/Core.h | 15 +++++- llvm/lib/IR/Core.cpp | 14 ++++++ llvm/test/Bindings/llvm-c/echo.ll | 18 +++++++ llvm/tools/llvm-c-test/echo.cpp | 80 ++++++++++++++++++++++++++----- llvm/tools/llvm-c-test/main.c | 9 ++-- 5 files changed, 119 insertions(+), 17 deletions(-) diff --git a/llvm/include/llvm-c/Core.h b/llvm/include/llvm-c/Core.h index 2c7b4c6eff107..34d23146be40f 100644 --- a/llvm/include/llvm-c/Core.h +++ b/llvm/include/llvm-c/Core.h @@ -3636,7 +3636,7 @@ void LLVMAddDestination(LLVMValueRef IndirectBr, LLVMBasicBlockRef Dest); /* Get the number of clauses on the landingpad instruction */ unsigned LLVMGetNumClauses(LLVMValueRef LandingPad); -/* Get the value of the clause at idnex Idx on the landingpad instruction */ +/* Get the value of the clause at index Idx on the landingpad instruction */ LLVMValueRef LLVMGetClause(LLVMValueRef LandingPad, unsigned Idx); /* Add a catch or filter clause to the landingpad instruction */ @@ -3937,6 +3937,19 @@ LLVMValueRef LLVMBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Ptr, LLVMAtomicOrdering FailureOrdering, LLVMBool SingleThread); +/** + * Get the number of elements in the mask of a ShuffleVector instruction. + */ +unsigned LLVMGetNumMaskElements(LLVMValueRef ShuffleVectorInst); + +/** + * Get the mask value at position Elt in the mask of a ShuffleVector + * instruction. Return LLVMUndefMaskElem if the mask value is undef at that + * position. + */ +int LLVMGetMaskValue(LLVMValueRef ShuffleVectorInst, unsigned Elt); +extern const int LLVMUndefMaskElem; + LLVMBool LLVMIsAtomicSingleThread(LLVMValueRef AtomicInst); void LLVMSetAtomicSingleThread(LLVMValueRef AtomicInst, LLVMBool SingleThread); diff --git a/llvm/lib/IR/Core.cpp b/llvm/lib/IR/Core.cpp index 6f3bbc80d4fd5..9caaea4b1f7a0 100644 --- a/llvm/lib/IR/Core.cpp +++ b/llvm/lib/IR/Core.cpp @@ -3952,6 +3952,20 @@ LLVMValueRef LLVMBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Ptr, singleThread ? SyncScope::SingleThread : SyncScope::System)); } +unsigned LLVMGetNumMaskElements(LLVMValueRef SVInst) { + Value *P = unwrap(SVInst); + ShuffleVectorInst *I = cast(P); + return I->getShuffleMask().size(); +} + +int LLVMGetMaskValue(LLVMValueRef SVInst, unsigned Elt) { + Value *P = unwrap(SVInst); + ShuffleVectorInst *I = cast(P); + return I->getMaskValue(Elt); +} +const int LLVMUndefMaskElem = + -1; // not actually accessible as ShuffleVectorInst::UndefMaskElem, so we + // hardcode it here LLVMBool LLVMIsAtomicSingleThread(LLVMValueRef AtomicInst) { Value *P = unwrap(AtomicInst); diff --git a/llvm/test/Bindings/llvm-c/echo.ll b/llvm/test/Bindings/llvm-c/echo.ll index 510798592b9d3..5494170f3cd65 100644 --- a/llvm/test/Bindings/llvm-c/echo.ll +++ b/llvm/test/Bindings/llvm-c/echo.ll @@ -156,6 +156,24 @@ define void @memops(i8* %ptr) { ret void } +define i32 @vectorops(i32, i32) { + %a = insertelement <4 x i32> undef, i32 %0, i32 0 + %b = insertelement <4 x i32> %a, i32 %1, i32 2 + %c = shufflevector <4 x i32> %b, <4 x i32> undef, <4 x i32> zeroinitializer + %d = shufflevector <4 x i32> %c, <4 x i32> %b, <4 x i32> + %e = add <4 x i32> %d, %a + %f = mul <4 x i32> %e, %b + %g = xor <4 x i32> %f, %d + %h = or <4 x i32> %f, %e + %i = lshr <4 x i32> %h, + %j = shl <4 x i32> %i, + %k = shufflevector <4 x i32> %j, <4 x i32> %i, <4 x i32> + %m = shufflevector <4 x i32> %k, <4 x i32> undef, <1 x i32> + %n = shufflevector <4 x i32> %j, <4 x i32> undef, <8 x i32> + %p = extractelement <8 x i32> %n, i32 5 + ret i32 %p +} + declare void @personalityFn() define void @exn() personality void ()* @personalityFn { diff --git a/llvm/tools/llvm-c-test/echo.cpp b/llvm/tools/llvm-c-test/echo.cpp index b254da28ddc4b..b404048749d39 100644 --- a/llvm/tools/llvm-c-test/echo.cpp +++ b/llvm/tools/llvm-c-test/echo.cpp @@ -30,7 +30,7 @@ template struct CAPIDenseMap {}; // The default DenseMapInfo require to know about pointer alignment. -// Because the C API uses opaques pointer types, their alignment is unknown. +// Because the C API uses opaque pointer types, their alignment is unknown. // As a result, we need to roll out our own implementation. template struct CAPIDenseMap { @@ -306,7 +306,7 @@ static LLVMValueRef clone_constant_impl(LLVMValueRef Cst, LLVMModuleRef M) { return LLVMConstArray(LLVMGetElementType(Ty), Elts.data(), EltCount); } - // Try contant data array + // Try constant data array if (LLVMIsAConstantDataArray(Cst)) { check_value_kind(Cst, LLVMConstantDataArrayValueKind); LLVMTypeRef Ty = TypeCloner(M).Clone(Cst); @@ -357,9 +357,32 @@ static LLVMValueRef clone_constant_impl(LLVMValueRef Cst, LLVMModuleRef M) { report_fatal_error("ConstantFP is not supported"); } - // This kind of constant is not supported + // Try ConstantVector + if (LLVMIsAConstantVector(Cst)) { + check_value_kind(Cst, LLVMConstantVectorValueKind); + LLVMTypeRef Ty = TypeCloner(M).Clone(Cst); + unsigned EltCount = LLVMGetVectorSize(Ty); + SmallVector Elts; + for (unsigned i = 0; i < EltCount; i++) + Elts.push_back(clone_constant(LLVMGetOperand(Cst, i), M)); + return LLVMConstVector(Elts.data(), EltCount); + } + + // Try ConstantDataVector + if (LLVMIsAConstantDataVector(Cst)) { + check_value_kind(Cst, LLVMConstantDataVectorValueKind); + LLVMTypeRef Ty = TypeCloner(M).Clone(Cst); + unsigned EltCount = LLVMGetVectorSize(Ty); + SmallVector Elts; + for (unsigned i = 0; i < EltCount; i++) + Elts.push_back(clone_constant(LLVMGetElementAsConstant(Cst, i), M)); + return LLVMConstVector(Elts.data(), EltCount); + } + + // At this point, if it's not a constant expression, it's a kind of constant + // which is not supported if (!LLVMIsAConstantExpr(Cst)) - report_fatal_error("Expected a constant expression"); + report_fatal_error("Unsupported constant kind"); // At this point, it must be a constant expression check_value_kind(Cst, LLVMConstantExprValueKind); @@ -370,7 +393,8 @@ static LLVMValueRef clone_constant_impl(LLVMValueRef Cst, LLVMModuleRef M) { return LLVMConstBitCast(clone_constant(LLVMGetOperand(Cst, 0), M), TypeCloner(M).Clone(Cst)); default: - fprintf(stderr, "%d is not a supported opcode\n", Op); + fprintf(stderr, "%d is not a supported opcode for constant expressions\n", + Op); exit(-1); } } @@ -443,7 +467,7 @@ struct FunCloner { auto i = VMap.find(Src); if (i != VMap.end()) { // If we have a hit, it means we already generated the instruction - // as a dependancy to somethign else. We need to make sure + // as a dependency to something else. We need to make sure // it is ordered properly. auto I = i->second; LLVMInstructionRemoveFromParent(I); @@ -746,8 +770,10 @@ struct FunCloner { } case LLVMExtractValue: { LLVMValueRef Agg = CloneValue(LLVMGetOperand(Src, 0)); - if (LLVMGetNumIndices(Src) != 1) - report_fatal_error("Expected only one indice"); + if (LLVMGetNumIndices(Src) > 1) + report_fatal_error("ExtractValue: Expected only one index"); + else if (LLVMGetNumIndices(Src) < 1) + report_fatal_error("ExtractValue: Expected an index"); auto I = LLVMGetIndices(Src)[0]; Dst = LLVMBuildExtractValue(Builder, Agg, I, Name); break; @@ -755,12 +781,44 @@ struct FunCloner { case LLVMInsertValue: { LLVMValueRef Agg = CloneValue(LLVMGetOperand(Src, 0)); LLVMValueRef V = CloneValue(LLVMGetOperand(Src, 1)); - if (LLVMGetNumIndices(Src) != 1) - report_fatal_error("Expected only one indice"); + if (LLVMGetNumIndices(Src) > 1) + report_fatal_error("InsertValue: Expected only one index"); + else if (LLVMGetNumIndices(Src) < 1) + report_fatal_error("InsertValue: Expected an index"); auto I = LLVMGetIndices(Src)[0]; Dst = LLVMBuildInsertValue(Builder, Agg, V, I, Name); break; } + case LLVMExtractElement: { + LLVMValueRef Agg = CloneValue(LLVMGetOperand(Src, 0)); + LLVMValueRef Index = CloneValue(LLVMGetOperand(Src, 1)); + Dst = LLVMBuildExtractElement(Builder, Agg, Index, Name); + break; + } + case LLVMInsertElement: { + LLVMValueRef Agg = CloneValue(LLVMGetOperand(Src, 0)); + LLVMValueRef V = CloneValue(LLVMGetOperand(Src, 1)); + LLVMValueRef Index = CloneValue(LLVMGetOperand(Src, 2)); + Dst = LLVMBuildInsertElement(Builder, Agg, V, Index, Name); + break; + } + case LLVMShuffleVector: { + LLVMValueRef Agg0 = CloneValue(LLVMGetOperand(Src, 0)); + LLVMValueRef Agg1 = CloneValue(LLVMGetOperand(Src, 1)); + SmallVector MaskElts; + unsigned NumMaskElts = LLVMGetNumMaskElements(Src); + for (unsigned i = 0; i < NumMaskElts; i++) { + int Val = LLVMGetMaskValue(Src, i); + if (Val == LLVMUndefMaskElem) { + MaskElts.push_back(LLVMGetUndef(LLVMInt64Type())); + } else { + MaskElts.push_back(LLVMConstInt(LLVMInt64Type(), Val, true)); + } + } + LLVMValueRef Mask = LLVMConstVector(MaskElts.data(), NumMaskElts); + Dst = LLVMBuildShuffleVector(Builder, Agg0, Agg1, Mask, Name); + break; + } case LLVMFreeze: { LLVMValueRef Arg = CloneValue(LLVMGetOperand(Src, 0)); Dst = LLVMBuildFreeze(Builder, Arg, Name); @@ -1102,7 +1160,7 @@ static void clone_symbols(LLVMModuleRef Src, LLVMModuleRef M) { LLVMGlobalSetMetadata(G, Kind, MD); } LLVMDisposeValueMetadataEntries(AllMetadata); - + LLVMSetGlobalConstant(G, LLVMIsGlobalConstant(Cur)); LLVMSetThreadLocal(G, LLVMIsThreadLocal(Cur)); LLVMSetExternallyInitialized(G, LLVMIsExternallyInitialized(Cur)); diff --git a/llvm/tools/llvm-c-test/main.c b/llvm/tools/llvm-c-test/main.c index 0be48930f0b65..5e8adb45d691e 100644 --- a/llvm/tools/llvm-c-test/main.c +++ b/llvm/tools/llvm-c-test/main.c @@ -36,10 +36,10 @@ static void print_usage(void) { fprintf(stderr, " * --targets-list\n"); fprintf(stderr, " List available targets\n\n"); fprintf(stderr, " * --object-list-sections\n"); - fprintf(stderr, " Read object file form stdin - list sections\n\n"); + fprintf(stderr, " Read object file from stdin - list sections\n\n"); fprintf(stderr, " * --object-list-symbols\n"); fprintf(stderr, - " Read object file form stdin - list symbols (like nm)\n\n"); + " Read object file from stdin - list symbols (like nm)\n\n"); fprintf(stderr, " * --disassemble\n"); fprintf(stderr, " Read lines of triple, hex ascii machine code from stdin " "- print disassembly\n\n"); @@ -48,11 +48,10 @@ static void print_usage(void) { stderr, " Read lines of name, rpn from stdin - print generated module\n\n"); fprintf(stderr, " * --echo\n"); - fprintf(stderr, - " Read bitcode file form stdin - print it back out\n\n"); + fprintf(stderr, " Read bitcode file from stdin - print it back out\n\n"); fprintf(stderr, " * --test-diagnostic-handler\n"); fprintf(stderr, - " Read bitcode file form stdin with a diagnostic handler set\n\n"); + " Read bitcode file from stdin with a diagnostic handler set\n\n"); fprintf(stderr, " * --test-dibuilder\n"); fprintf(stderr, " Run tests for the DIBuilder C API - print generated module\n\n"); From 293924973057e33fcc63521f582bb9fd41e60cc4 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sat, 26 Sep 2020 17:32:38 -0600 Subject: [PATCH 17/25] [LLVM-C] Turn a ShuffleVector Constant Into a Getter. It is not a good idea to expose raw constants in the LLVM C API. Replace this with an explicit getter. Differential Revision: https://reviews.llvm.org/D88367 (cherry picked from commit 55f727306e727ea9f013d09c9b8aa70dbce6a1bd) --- llvm/include/llvm-c/Core.h | 13 ++++++++++--- llvm/lib/IR/Core.cpp | 5 ++--- llvm/tools/llvm-c-test/echo.cpp | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/llvm/include/llvm-c/Core.h b/llvm/include/llvm-c/Core.h index 34d23146be40f..c8a6f970419b3 100644 --- a/llvm/include/llvm-c/Core.h +++ b/llvm/include/llvm-c/Core.h @@ -3942,13 +3942,20 @@ LLVMValueRef LLVMBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Ptr, */ unsigned LLVMGetNumMaskElements(LLVMValueRef ShuffleVectorInst); +/** + * \returns a constant that specifies that the result of a \c ShuffleVectorInst + * is undefined. + */ +int LLVMGetUndefMaskElem(void); + /** * Get the mask value at position Elt in the mask of a ShuffleVector - * instruction. Return LLVMUndefMaskElem if the mask value is undef at that - * position. + * instruction. + * + * \Returns the result of \c LLVMGetUndefMaskElem() if the mask value is undef + * at that position. */ int LLVMGetMaskValue(LLVMValueRef ShuffleVectorInst, unsigned Elt); -extern const int LLVMUndefMaskElem; LLVMBool LLVMIsAtomicSingleThread(LLVMValueRef AtomicInst); void LLVMSetAtomicSingleThread(LLVMValueRef AtomicInst, LLVMBool SingleThread); diff --git a/llvm/lib/IR/Core.cpp b/llvm/lib/IR/Core.cpp index 9caaea4b1f7a0..c1f7329034e09 100644 --- a/llvm/lib/IR/Core.cpp +++ b/llvm/lib/IR/Core.cpp @@ -3963,9 +3963,8 @@ int LLVMGetMaskValue(LLVMValueRef SVInst, unsigned Elt) { ShuffleVectorInst *I = cast(P); return I->getMaskValue(Elt); } -const int LLVMUndefMaskElem = - -1; // not actually accessible as ShuffleVectorInst::UndefMaskElem, so we - // hardcode it here + +int LLVMGetUndefMaskElem(void) { return UndefMaskElem; } LLVMBool LLVMIsAtomicSingleThread(LLVMValueRef AtomicInst) { Value *P = unwrap(AtomicInst); diff --git a/llvm/tools/llvm-c-test/echo.cpp b/llvm/tools/llvm-c-test/echo.cpp index b404048749d39..0b3a10f463dd3 100644 --- a/llvm/tools/llvm-c-test/echo.cpp +++ b/llvm/tools/llvm-c-test/echo.cpp @@ -809,7 +809,7 @@ struct FunCloner { unsigned NumMaskElts = LLVMGetNumMaskElements(Src); for (unsigned i = 0; i < NumMaskElts; i++) { int Val = LLVMGetMaskValue(Src, i); - if (Val == LLVMUndefMaskElem) { + if (Val == LLVMGetUndefMaskElem()) { MaskElts.push_back(LLVMGetUndef(LLVMInt64Type())); } else { MaskElts.push_back(LLVMConstInt(LLVMInt64Type(), Val, true)); From eb83b551d3eb08cf472fe6307fe3809a8005b2cc Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Mon, 28 Sep 2020 15:35:01 +0200 Subject: [PATCH 18/25] Fix mysterious failure of SupportTests FileCheckTest.Binop The test would fail in no-asserts release builds using MSVC for 64-bit Windows: Unexpected error message: TestBuffer:1:1: error: implicit format conflict between 'FOO' (%u) and '18\0' (%x), need an explicit format specifier Error message(s) not found: {implicit format conflict between 'FOO' (%u) and 'BAZ' (%x), need an explicit format specifier} It seems a string from a previous test case is finding its way into the latter one. This doesn't reproduce on master anymore after 998709b7d, so let's just hack around it here for the branch. --- llvm/unittests/Support/FileCheckTest.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/llvm/unittests/Support/FileCheckTest.cpp b/llvm/unittests/Support/FileCheckTest.cpp index 92975dcd76b74..a4591bc319bbb 100644 --- a/llvm/unittests/Support/FileCheckTest.cpp +++ b/llvm/unittests/Support/FileCheckTest.cpp @@ -714,6 +714,7 @@ TEST_F(FileCheckTest, Binop) { Value = Binop.eval(); expectUndefErrors({"FOO", "BAR"}, Value.takeError()); + { // Literal + Variable has format of variable. ExprStr = bufferize(SM, "FOO+18"); FooStr = ExprStr.take_front(3); @@ -736,6 +737,7 @@ TEST_F(FileCheckTest, Binop) { ImplicitFormat = Binop.getImplicitFormat(SM); ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded()); EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned); + } // Variables with different implicit format conflict. ExprStr = bufferize(SM, "FOO+BAZ"); From 2875ec773d20ecf796a7e532532bdfc90c64ef94 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Fri, 25 Sep 2020 10:08:24 -0700 Subject: [PATCH 19/25] Fix DISubprogram-v4.ll after e17f52d623cc146b7d9bf5a2e02965043508b4c4 (cherry picked from commit 7d0556fc137aa07347741b7750e50ecbc2b4c6e2) --- llvm/test/Bitcode/DISubprogram-v4.ll | 4 ++-- llvm/test/Bitcode/DISubprogram-v4.ll.bc | Bin 1372 -> 1336 bytes 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/test/Bitcode/DISubprogram-v4.ll b/llvm/test/Bitcode/DISubprogram-v4.ll index ce5521002cafb..9e5caecd7361a 100644 --- a/llvm/test/Bitcode/DISubprogram-v4.ll +++ b/llvm/test/Bitcode/DISubprogram-v4.ll @@ -24,8 +24,8 @@ define void @_Z3foov() !dbg !9 { !8 = distinct !DICompileUnit(language: DW_LANG_C99, producer: "clang", file: !2, isOptimized: true, flags: "-O2") -; CHECK: !9 = !DISubprogram(scope: null, spFlags: 0) -!9 = !DISubprogram(isDefinition: false) +; CHECK: !9 = distinct !DISubprogram(scope: null, spFlags: 0) +!9 = distinct !DISubprogram(isDefinition: false) ; CHECK: !10 = distinct !DISubprogram({{.*}}, spFlags: DISPFlagPureVirtual | DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, !10 = distinct !DISubprogram(name: "foo", linkageName: "_Zfoov", scope: !1, diff --git a/llvm/test/Bitcode/DISubprogram-v4.ll.bc b/llvm/test/Bitcode/DISubprogram-v4.ll.bc index 392df4aae74d3fb0df0c2196191496a4eb4045dd..41bbcf874ac144ac97196fdf8d5e1300a91441b1 100644 GIT binary patch delta 473 zcmcb^wS&vh$@#!NQxOIRRt5%!Bpw45&&Ykx-d(oSbzw9D2{9=0Ffdp%GB9v6DKant z`N9(o4df??C^)-yEM<^1a$@o1n=n!FNPuHtW6#6pr(GiE@xcyc2{BI*j*l`< W%g-+}*E7&FuqiG{$;`6@s{sH{`fMZs delta 509 zcmdnNb%)E)$@#!NQxOIRRt5%!Bpw45pU8b`?=RcwhV}}uo&bs~@-Q&iF)}c4Gbu7K z0o902G&ImIIHIWRBGld^;5dmXC@!v0SUmYVQz7H6$(hUw^%|`UWupZcx)~T6nSn<7gz|bNW*iq|IUvN~@Bm~- zaAsio_qij zff>U9GzJVH#>7C)VL&oR8ez^E0S0DK2L*-$J}nF>2STPo^};BF!woW>JuGS(+}k&7 zGujlWrgPFHHE~W_^#YGsD^Bg2oWjzU*2s1Qs2t>~XqE(!7a0l_7#KhdBR&D87KYuR zXaIv)VBGKmSwhTH(m(?7QO0Tc`DGS*26_g?WqAtenI*br#>uHE7Dj0nmL@jEB`KMC Ic3`ax0J7bADF6Tf From 815c690f207529286f5d78049692ec1356d041f8 Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Fri, 25 Sep 2020 13:09:47 -0700 Subject: [PATCH 20/25] [ubsan] nullability-arg: Fix crash on C++ member pointers Extend -fsanitize=nullability-arg to handle call sites which accept C++ member pointers. rdar://62476022 Differential Revision: https://reviews.llvm.org/D88336 (cherry picked from commit 06bc685fa2400cc28282ab6dd3c917d45bfa662f) --- clang/lib/CodeGen/CGCall.cpp | 5 +- clang/lib/CodeGen/CGExpr.cpp | 7 +++ clang/lib/CodeGen/CodeGenFunction.h | 3 ++ .../test/CodeGenCXX/ubsan-nullability-arg.cpp | 51 +++++++++++++++++++ 4 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 clang/test/CodeGenCXX/ubsan-nullability-arg.cpp diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 69d4a60705718..88ad08279cc07 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -3746,10 +3746,7 @@ void CodeGenFunction::EmitNonNullArgCheck(RValue RV, QualType ArgType, } SanitizerScope SanScope(this); - assert(RV.isScalar()); - llvm::Value *V = RV.getScalarVal(); - llvm::Value *Cond = - Builder.CreateICmpNE(V, llvm::Constant::getNullValue(V->getType())); + llvm::Value *Cond = EmitNonNullRValueCheck(RV, ArgType); llvm::Constant *StaticData[] = { EmitCheckSourceLocation(ArgLoc), EmitCheckSourceLocation(AttrLoc), llvm::ConstantInt::get(Int32Ty, ArgNo + 1), diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index c6cfaf6fe9d6e..b0c44eda43515 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1170,6 +1170,13 @@ Address CodeGenFunction::EmitPointerWithAlignment(const Expr *E, return Address(EmitScalarExpr(E), Align); } +llvm::Value *CodeGenFunction::EmitNonNullRValueCheck(RValue RV, QualType T) { + llvm::Value *V = RV.getScalarVal(); + if (auto MPT = T->getAs()) + return CGM.getCXXABI().EmitMemberPointerIsNotNull(*this, V, MPT); + return Builder.CreateICmpNE(V, llvm::Constant::getNullValue(V->getType())); +} + RValue CodeGenFunction::GetUndefRValue(QualType Ty) { if (Ty->isVoidType()) return RValue::get(nullptr); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index d3b11618d5129..cb903a1f9d880 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3563,6 +3563,9 @@ class CodeGenFunction : public CodeGenTypeCache { // LValue Expression Emission //===--------------------------------------------------------------------===// + /// Create a check that a scalar RValue is non-null. + llvm::Value *EmitNonNullRValueCheck(RValue RV, QualType T); + /// GetUndefRValue - Get an appropriate 'undef' rvalue for the given type. RValue GetUndefRValue(QualType Ty); diff --git a/clang/test/CodeGenCXX/ubsan-nullability-arg.cpp b/clang/test/CodeGenCXX/ubsan-nullability-arg.cpp new file mode 100644 index 0000000000000..fbebd153a9eae --- /dev/null +++ b/clang/test/CodeGenCXX/ubsan-nullability-arg.cpp @@ -0,0 +1,51 @@ +// RUN: %clang_cc1 -x c++ -triple x86_64-apple-darwin10 -emit-llvm -o - %s -fsanitize=nullability-arg | FileCheck %s -check-prefixes=ITANIUM,ALL +// RUN: %clang_cc1 -x c++ -triple x86_64-pc-windows-msvc -emit-llvm -o - %s -fsanitize=nullability-arg | FileCheck %s -check-prefixes=MSVC,ALL + +namespace method_ptr { + +struct S0 { + void foo1(); +}; + +void foo1(void (S0::*_Nonnull f)()); + +// ITANIUM-LABEL: @_ZN10method_ptr5test1Ev(){{.*}} { +// ITANIUM: br i1 icmp ne (i64 ptrtoint (void (%"struct.method_ptr::S0"*)* @_ZN10method_ptr2S04foo1Ev to i64), i64 0), label %[[CONT:.*]], label %[[FAIL:[^,]*]] +// ITANIUM-EMPTY: +// ITANIUM-NEXT: [[FAIL]]: +// ITANIUM-NEXT: call void @__ubsan_handle_nullability_arg + +// MSVC-LABEL: @"?test1@method_ptr@@YAXXZ"(){{.*}} { +// MSVC: br i1 true, label %[[CONT:.*]], label %[[FAIL:[^,]*]] +// MSVC-EMPTY: +// MSVC-NEXT: [[FAIL]]: +// MSVC-NEXT: call void @__ubsan_handle_nullability_arg +void test1() { + foo1(&S0::foo1); +} + +} // namespace method_ptr + +namespace data_ptr { + +struct S0 { + int field1; +}; + +using member_ptr = int S0::*; + +void foo1(member_ptr _Nonnull); + +// ITANIUM-LABEL: @_ZN8data_ptr5test1ENS_2S0E( +// MSVC-LABEL: @"?test1@data_ptr@@YAXUS0@1@@Z"( +// ALL: [[DATA_PTR_CHECK:%.*]] = icmp ne {{.*}}, -1, !nosanitize +// ALL-NEXT: br i1 [[DATA_PTR_CHECK]], label %[[CONT:.*]], label %[[FAIL:[^,]+]] +// ALL-EMPTY: +// ALL-NEXT: [[FAIL]]: +// ALL-NEXT: call void @__ubsan_handle_nullability_arg +void test1(S0 s) { + int S0::*member = &S0::field1; + foo1(member); +} + +} // namespace data_ptr From afada41144add1d1813514c30edd564e788da0ba Mon Sep 17 00:00:00 2001 From: Michelle Casbon Date: Fri, 25 Sep 2020 22:18:30 +0000 Subject: [PATCH 21/25] Add missing NodeType enums --- llvm/lib/Target/AArch64/AArch64ISelLowering.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index d9a2514378273..9ce0e16dfc6f4 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -1380,6 +1380,8 @@ const char *AArch64TargetLowering::getTargetNodeName(unsigned Opcode) const { case AArch64ISD::FIRST_NUMBER: break; MAKE_CASE(AArch64ISD::CALL) + MAKE_CASE(AArch64ISD::AUTH_CALL) + MAKE_CASE(AArch64ISD::AUTH_TC_RETURN) MAKE_CASE(AArch64ISD::ADRP) MAKE_CASE(AArch64ISD::ADR) MAKE_CASE(AArch64ISD::ADDlow) From 8994b1f3d3c2ab4729fc3d429b56bf0065aa8e16 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Mon, 28 Sep 2020 16:56:38 -0700 Subject: [PATCH 22/25] Once we've found a firmware binary and loaded it, don't search more (#1872) Add the flag in ProcessMachCore::DoLoadCore that stops additional searches for the binaries when we have an LC_NOTE identifying the firmware/standalone binary as the correct one & we have loaded it successfully. (cherry picked from commit 6e54918db7f4dad0d5a6fbff140009ed6f151d2c) --- lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp index bee161bde96b5..744942115016e 100644 --- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp +++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp @@ -398,6 +398,7 @@ Status ProcessMachCore::DoLoadCore() { added_module.Append(module_sp, false); GetTarget().ModulesDidLoad(added_module); m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); + found_main_binary_definitively = true; } } } From 861da5bd520fe2875bf41692addc5ff4d564ccae Mon Sep 17 00:00:00 2001 From: Jan Korous Date: Wed, 16 Sep 2020 09:52:51 -0700 Subject: [PATCH 23/25] [clang] Selectively ena/disa-ble format-insufficient-args warning Differential Revision: https://reviews.llvm.org/D87176 --- clang/include/clang/Basic/DiagnosticGroups.td | 4 +++- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/test/Misc/warning-wall.c | 1 + clang/test/Sema/warn-printf-insufficient-data-args.c | 11 +++++++++++ 4 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 clang/test/Sema/warn-printf-insufficient-data-args.c diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 5e0031335e1ce..6dc2e51b434ca 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -234,6 +234,7 @@ def ExtraSemi : DiagGroup<"extra-semi", [CXX98CompatExtraSemi, def GNUFlexibleArrayInitializer : DiagGroup<"gnu-flexible-array-initializer">; def GNUFlexibleArrayUnionMember : DiagGroup<"gnu-flexible-array-union-member">; def GNUFoldingConstant : DiagGroup<"gnu-folding-constant">; +def FormatInsufficientArgs : DiagGroup<"format-insufficient-args">; def FormatExtraArgs : DiagGroup<"format-extra-args">; def FormatZeroLength : DiagGroup<"format-zero-length">; @@ -840,7 +841,8 @@ def FormatPedantic : DiagGroup<"format-pedantic">; def FormatTypeConfusion : DiagGroup<"format-type-confusion">; def Format : DiagGroup<"format", [FormatExtraArgs, FormatZeroLength, NonNull, - FormatSecurity, FormatY2K, FormatInvalidSpecifier]>, + FormatSecurity, FormatY2K, FormatInvalidSpecifier, + FormatInsufficientArgs]>, DiagCategory<"Format String Issue">; def FormatNonLiteral : DiagGroup<"format-nonliteral">; def Format2 : DiagGroup<"format=2", diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index c8cc44b4feb35..685a735bd4518 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8945,7 +8945,7 @@ def note_array_declared_here : Note< "array %0 declared here">; def warn_printf_insufficient_data_args : Warning< - "more '%%' conversions than data arguments">, InGroup; + "more '%%' conversions than data arguments">, InGroup; def warn_printf_data_arg_not_used : Warning< "data argument not used by format string">, InGroup; def warn_format_invalid_conversion : Warning< diff --git a/clang/test/Misc/warning-wall.c b/clang/test/Misc/warning-wall.c index c63d4beecff04..9975323f70286 100644 --- a/clang/test/Misc/warning-wall.c +++ b/clang/test/Misc/warning-wall.c @@ -9,6 +9,7 @@ CHECK-NEXT: -Wdelete-non-virtual-dtor CHECK-NEXT: -Wdelete-non-abstract-non-virtual-dtor CHECK-NEXT: -Wdelete-abstract-non-virtual-dtor CHECK-NEXT: -Wformat +CHECK-NEXT: -Wformat-insufficient-args CHECK-NEXT: -Wformat-extra-args CHECK-NEXT: -Wformat-zero-length CHECK-NEXT: -Wnonnull diff --git a/clang/test/Sema/warn-printf-insufficient-data-args.c b/clang/test/Sema/warn-printf-insufficient-data-args.c new file mode 100644 index 0000000000000..a8de7118e8b1f --- /dev/null +++ b/clang/test/Sema/warn-printf-insufficient-data-args.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -verify=WARNING-ON %s +// RUN: %clang_cc1 -fsyntax-only -Wno-format-insufficient-args -verify=WARNING-OFF %s + + +int printf(const char * format, ...); + +int main(void) { + int patatino = 42; + printf("%i %i", patatino); // WARNING-ON-warning {{more '%' conversions than data arguments}} + // WARNING-OFF-no-diagnostics +} From b3b219d6718716e8676c8876e5e87f34339c6cd3 Mon Sep 17 00:00:00 2001 From: Jan Korous Date: Mon, 28 Sep 2020 17:19:31 -0700 Subject: [PATCH 24/25] [clang] Update warning-wall.c test Follow-up to 1e86d637eb4f: [clang] Selectively ena/disa-ble format-insufficient-args warning --- clang/test/Misc/warning-wall.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Misc/warning-wall.c b/clang/test/Misc/warning-wall.c index 9975323f70286..ac2b124e3312f 100644 --- a/clang/test/Misc/warning-wall.c +++ b/clang/test/Misc/warning-wall.c @@ -9,13 +9,13 @@ CHECK-NEXT: -Wdelete-non-virtual-dtor CHECK-NEXT: -Wdelete-non-abstract-non-virtual-dtor CHECK-NEXT: -Wdelete-abstract-non-virtual-dtor CHECK-NEXT: -Wformat -CHECK-NEXT: -Wformat-insufficient-args CHECK-NEXT: -Wformat-extra-args CHECK-NEXT: -Wformat-zero-length CHECK-NEXT: -Wnonnull CHECK-NEXT: -Wformat-security CHECK-NEXT: -Wformat-y2k CHECK-NEXT: -Wformat-invalid-specifier +CHECK-NEXT: -Wformat-insufficient-args CHECK-NEXT: -Wfor-loop-analysis CHECK-NEXT: -Wframe-address CHECK-NEXT: -Wimplicit From dda0a1867cc0c4ace4535f179aec85c3ff8cfa96 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Tue, 29 Sep 2020 14:58:46 +0200 Subject: [PATCH 25/25] [LLVM 11] Add SystemZ changes to release notes Differential Revision: https://reviews.llvm.org/D88479 --- llvm/docs/ReleaseNotes.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst index d724ba09502a5..db64fa2810180 100644 --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -241,6 +241,21 @@ Bug fixes: * The correct libcall is now emitted for converting a float/double to a 32-bit signed or unsigned integer on RV64 targets lacking the F or D extensions. +Changes to the SystemZ Target +----------------------------- + +* Added support for the MemorySanitizer and the LeakSanitizer. +* Added support for the ``-fstack-clash-protection`` command line option. +* Enhanced the assembler parser to allow using `%r0` even in an address + register context, and to allow specifying registers using plain integer + numbers instead of register names everywhere. +* Fixed wrong code generation violating the platform ABI when passing + a C++ class (not struct) type having only a single member of + floating-point type. +* Fixed wrong code generation when using the `vec_store_len_r` or + `vec_load_len_r` intrinsics with an immediate length argument of + 16 or larger. +* Miscellaneous codegen enhancements, in particular to improve vector code. Changes to the X86 Target -------------------------