Index: lldb/source/Commands/CommandObjectWatchpointCommand.cpp =================================================================== --- lldb/source/Commands/CommandObjectWatchpointCommand.cpp +++ lldb/source/Commands/CommandObjectWatchpointCommand.cpp @@ -422,7 +422,8 @@ // automatize what the user would do manually: make their watchpoint // command be a function call else if (!m_options.m_function_name.empty()) { - std::string oneliner(m_options.m_function_name); + std::string oneliner("return "); + oneliner += m_options.m_function_name; oneliner += "(frame, wp, internal_dict)"; script_interp->SetWatchpointCommandCallback( wp_options, oneliner.c_str()); Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -1292,40 +1292,52 @@ StringList auto_generated_function; auto_generated_function.AppendString(signature); auto_generated_function.AppendString( - " global_dict = globals()"); // Grab the global dictionary + " global_dict = globals()"); // Grab the global dictionary auto_generated_function.AppendString( - " new_keys = internal_dict.keys()"); // Make a list of keys in the - // session dict + " new_keys = internal_dict.keys()"); // Make a list of keys in the + // session dict auto_generated_function.AppendString( - " old_keys = global_dict.keys()"); // Save list of keys in global dict + " old_keys = global_dict.keys()"); // Save list of keys in global dict auto_generated_function.AppendString( - " global_dict.update (internal_dict)"); // Add the session dictionary - // to the - // global dictionary. + " global_dict.update (internal_dict)"); // Add the session dictionary + // to the global dictionary. + auto_generated_function.AppendString( + " __return_val = None"); // Initialize user callback return value. // Wrap everything up inside the function, increasing the indentation. - auto_generated_function.AppendString(" if True:"); + auto_generated_function.AppendString(" def __user_callback():"); + auto_generated_function.AppendString(" nonlocal __return_val"); for (int i = 0; i < num_lines; ++i) { sstr.Clear(); - sstr.Printf(" %s", input.GetStringAtIndex(i)); + llvm::StringRef line = input.GetStringAtIndex(i); + if (line.startswith("return ")) { + llvm::StringRef return_val = + line.substr(llvm::StringRef("return ").size()); + sstr.Printf(" __return_val = %s", return_val.data()); + } else { + sstr.Printf(" %s", line.data()); + } auto_generated_function.AppendString(sstr.GetData()); } auto_generated_function.AppendString( - " for key in new_keys:"); // Iterate over all the keys from session - // dict + " __user_callback()"); // Call user callback auto_generated_function.AppendString( - " internal_dict[key] = global_dict[key]"); // Update session dict - // values + " for key in new_keys:"); // Iterate over all the keys from session + // dict auto_generated_function.AppendString( - " if key not in old_keys:"); // If key was not originally in - // global dict + " internal_dict[key] = global_dict[key]"); // Update session dict + // values auto_generated_function.AppendString( - " del global_dict[key]"); // ...then remove key/value from - // global dict + " if key not in old_keys:"); // If key was not originally in + // global dict + auto_generated_function.AppendString( + " del global_dict[key]"); // ...then remove key/value from + // global dict + auto_generated_function.AppendString( + " return __return_val"); // Return the user callback return value. // Verify that the results are valid Python. - error = ExportFunctionDefinitionToInterpreter(auto_generated_function); return error; Index: lldb/test/API/commands/watchpoints/watchpoint_commands/command/TestWatchpointCommandPython.py =================================================================== --- lldb/test/API/commands/watchpoints/watchpoint_commands/command/TestWatchpointCommandPython.py +++ lldb/test/API/commands/watchpoints/watchpoint_commands/command/TestWatchpointCommandPython.py @@ -1,4 +1,4 @@ -""" +""" Test 'watchpoint command'. """ @@ -22,6 +22,8 @@ # Find the line number to break inside main(). self.line = line_number( self.source, '// Set break point at this line.') + self.second_line = line_number( + self.source, '// Set another breakpoint here.') # And the watchpoint variable declaration line number. self.decl = line_number(self.source, '// Watchpoint variable declaration.') @@ -143,6 +145,32 @@ self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT, substrs=['stop reason = watchpoint']) + # We should have hit the watchpoint once, set cookie to 888, since the + # user callback returned True. + self.expect("frame variable --show-globals cookie", + substrs=['(int32_t)', 'cookie = 888']) + + self.runCmd("process continue") + + # We should be stopped again due to the watchpoint (write type). + # The stop reason of the thread should be watchpoint. + self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT, + substrs=['stop reason = watchpoint']) + + # We should have hit the watchpoint a second time, set cookie to 666, + # even if the user callback didn't return anything and then continue. + self.expect("frame variable --show-globals cookie", + substrs=['(int32_t)', 'cookie = 666']) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line( + self, None, self.second_line, num_expected_locations=1) + + self.runCmd("process continue") + + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stop reason = breakpoint']) + # We should have hit the watchpoint once, set cookie to 888, then continued to the # second hit and set it to 999 self.expect("frame variable --show-globals cookie", Index: lldb/test/API/commands/watchpoints/watchpoint_commands/command/main.cpp =================================================================== --- lldb/test/API/commands/watchpoints/watchpoint_commands/command/main.cpp +++ lldb/test/API/commands/watchpoints/watchpoint_commands/command/main.cpp @@ -16,5 +16,5 @@ modify(global); printf("global=%d\n", global); - printf("cookie=%d\n", cookie); + printf("cookie=%d\n", cookie); // Set another breakpoint here. } Index: lldb/test/API/commands/watchpoints/watchpoint_commands/command/watchpoint_command.py =================================================================== --- lldb/test/API/commands/watchpoints/watchpoint_commands/command/watchpoint_command.py +++ lldb/test/API/commands/watchpoints/watchpoint_commands/command/watchpoint_command.py @@ -9,7 +9,12 @@ print ("I stopped the first time") frame.EvaluateExpression("cookie = 888") num_hits += 1 - frame.thread.process.Continue() + return True + if num_hits == 1: + print ("I stopped the second time, but with no return") + frame.EvaluateExpression("cookie = 666") + num_hits += 1 else: print ("I stopped the %d time" % (num_hits)) frame.EvaluateExpression("cookie = 999") + return False # This cause the process to continue.