Skip to content

Commit 3dbf346

Browse files
committed
Optimized the way breakpoint conditions are evaluated.
Previously, the options for a breakopint or its locations stored only the text of the breakpoint condition (ironically, they used ClangUserExpression as a glorified std::string) and, each time the condition had to be evaluated in the StopInfo code, the expression parser would be invoked via a static method to parse and then execute the expression. I made several changes here: - Each breakpoint location now has its own ClangUserExpressionSP containing a version of the breakpoint expression compiled for that exact location. - Whenever the breakpoint is hit, the breakpoint condition expression is simply re-run to determine whether to stop. - If the process changes (e.g., it's re-run) or the source code of the expression changes (we use a hash so as to avoid doing string comparisons) the ClangUserExpressionSP is re-generated. This should improve performance of breakpoint conditions significantly, and takes advantage of the recent expression re-use work. llvm-svn: 179838
1 parent cf5e5ba commit 3dbf346

File tree

7 files changed

+201
-103
lines changed

7 files changed

+201
-103
lines changed

lldb/include/lldb/Breakpoint/BreakpointLocation.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "lldb/Core/Address.h"
2525
#include "lldb/Target/Process.h"
2626
#include "lldb/Core/StringList.h"
27+
#include "lldb/Expression/ClangUserExpression.h"
2728

2829
namespace lldb_private {
2930

@@ -175,7 +176,10 @@ class BreakpointLocation :
175176
// condition has been set.
176177
//------------------------------------------------------------------
177178
const char *
178-
GetConditionText () const;
179+
GetConditionText (size_t *hash = NULL) const;
180+
181+
bool
182+
ConditionSaysStop (ExecutionContext &exe_ctx, Error &error);
179183

180184

181185
//------------------------------------------------------------------
@@ -381,6 +385,8 @@ class BreakpointLocation :
381385
Breakpoint &m_owner; ///< The breakpoint that produced this object.
382386
std::unique_ptr<BreakpointOptions> m_options_ap; ///< Breakpoint options pointer, NULL if we're using our breakpoint's options.
383387
lldb::BreakpointSiteSP m_bp_site_sp; ///< Our breakpoint site (it may be shared by more than one location.)
388+
ClangUserExpression::ClangUserExpressionSP m_user_expression_sp; ///< The compiled expression to use in testing our condition.
389+
size_t m_condition_hash; ///< For testing whether the condition source code changed.
384390

385391
void
386392
SendBreakpointLocationChangedEvent (lldb::BreakpointEventType eventKind);

lldb/include/lldb/Breakpoint/BreakpointOptions.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ class BreakpointOptions
183183
/// A pointer to the condition expression text, or NULL if no
184184
// condition has been set.
185185
//------------------------------------------------------------------
186-
const char *GetConditionText () const;
186+
const char *GetConditionText (size_t *hash = NULL) const;
187187

188188
//------------------------------------------------------------------
189189
// Enabled/Ignore Count
@@ -349,8 +349,8 @@ class BreakpointOptions
349349
bool m_one_shot;
350350
uint32_t m_ignore_count; // Number of times to ignore this breakpoint
351351
std::unique_ptr<ThreadSpec> m_thread_spec_ap; // Thread for which this breakpoint will take
352-
std::unique_ptr<ClangUserExpression> m_condition_ap; // The condition to test.
353-
352+
std::string m_condition_text; // The condition to test.
353+
size_t m_condition_text_hash; // Its hash, so that locations know when the condition is updated.
354354
};
355355

356356
} // namespace lldb_private

lldb/include/lldb/Expression/ClangUserExpression.h

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include "lldb/lldb-forward.h"
2323
#include "lldb/lldb-private.h"
24+
#include "lldb/Core/Address.h"
2425
#include "lldb/Core/ClangForward.h"
2526
#include "lldb/Expression/ClangExpression.h"
2627
#include "lldb/Expression/ClangExpressionVariable.h"
@@ -112,6 +113,9 @@ class ClangUserExpression : public ClangExpression
112113
return m_can_interpret;
113114
}
114115

116+
bool
117+
MatchesContext (ExecutionContext &exe_ctx);
118+
115119
//------------------------------------------------------------------
116120
/// Execute the parsed expression
117121
///
@@ -383,34 +387,16 @@ class ClangUserExpression : public ClangExpression
383387
lldb::addr_t &cmd_ptr);
384388

385389
void
386-
InstallContext (ExecutionContext &exe_ctx)
387-
{
388-
m_process_wp = exe_ctx.GetProcessSP();
389-
m_target_wp = exe_ctx.GetTargetSP();
390-
m_frame_wp = exe_ctx.GetFrameSP();
391-
}
390+
InstallContext (ExecutionContext &exe_ctx);
392391

393392
bool
394393
LockAndCheckContext (ExecutionContext &exe_ctx,
395394
lldb::TargetSP &target_sp,
396395
lldb::ProcessSP &process_sp,
397-
lldb::StackFrameSP &frame_sp)
398-
{
399-
target_sp = m_target_wp.lock();
400-
process_sp = m_process_wp.lock();
401-
frame_sp = m_frame_wp.lock();
402-
403-
if ((target_sp && target_sp.get() != exe_ctx.GetTargetPtr()) ||
404-
(process_sp && process_sp.get() != exe_ctx.GetProcessPtr()) ||
405-
(frame_sp && frame_sp.get() != exe_ctx.GetFramePtr()))
406-
return false;
407-
408-
return true;
409-
}
396+
lldb::StackFrameSP &frame_sp);
410397

411-
lldb::TargetWP m_target_wp; ///< The target used as the context for the expression.
412398
lldb::ProcessWP m_process_wp; ///< The process used as the context for the expression.
413-
lldb::StackFrameWP m_frame_wp; ///< The stack frame used as context for the expression.
399+
Address m_address; ///< The address the process is stopped in.
414400

415401
std::string m_expr_text; ///< The text of the expression, as typed by the user
416402
std::string m_expr_prefix; ///< The text of the translation-level definitions, as provided by the user

lldb/source/Breakpoint/BreakpointLocation.cpp

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,109 @@ BreakpointLocation::SetCondition (const char *condition)
240240
}
241241

242242
const char *
243-
BreakpointLocation::GetConditionText () const
243+
BreakpointLocation::GetConditionText (size_t *hash) const
244244
{
245-
return GetOptionsNoCreate()->GetConditionText();
245+
return GetOptionsNoCreate()->GetConditionText(hash);
246+
}
247+
248+
bool
249+
BreakpointLocation::ConditionSaysStop (ExecutionContext &exe_ctx, Error &error)
250+
{
251+
Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS);
252+
253+
size_t condition_hash;
254+
const char *condition_text = GetConditionText(&condition_hash);
255+
256+
if (!condition_text)
257+
return false;
258+
259+
if (condition_hash != m_condition_hash ||
260+
!m_user_expression_sp ||
261+
!m_user_expression_sp->MatchesContext(exe_ctx))
262+
{
263+
m_user_expression_sp.reset(new ClangUserExpression(condition_text,
264+
NULL,
265+
lldb::eLanguageTypeUnknown,
266+
ClangUserExpression::eResultTypeAny));
267+
268+
StreamString errors;
269+
270+
if (!m_user_expression_sp->Parse(errors,
271+
exe_ctx,
272+
eExecutionPolicyOnlyWhenNeeded,
273+
true))
274+
{
275+
error.SetErrorStringWithFormat("Couldn't parse conditional expression:\n%s",
276+
errors.GetData());
277+
m_user_expression_sp.reset();
278+
return false;
279+
}
280+
281+
m_condition_hash = condition_hash;
282+
}
283+
284+
// We need to make sure the user sees any parse errors in their condition, so we'll hook the
285+
// constructor errors up to the debugger's Async I/O.
286+
287+
ValueObjectSP result_value_sp;
288+
const bool unwind_on_error = true;
289+
const bool ignore_breakpoints = true;
290+
const bool try_all_threads = true;
291+
292+
Error expr_error;
293+
294+
StreamString execution_errors;
295+
296+
ClangExpressionVariableSP result_variable_sp;
297+
298+
ExecutionResults result_code =
299+
m_user_expression_sp->Execute(execution_errors,
300+
exe_ctx,
301+
unwind_on_error,
302+
ignore_breakpoints,
303+
m_user_expression_sp,
304+
result_variable_sp,
305+
try_all_threads,
306+
ClangUserExpression::kDefaultTimeout);
307+
308+
bool ret;
309+
310+
if (result_code == eExecutionCompleted)
311+
{
312+
result_value_sp = result_variable_sp->GetValueObject();
313+
314+
if (result_value_sp)
315+
{
316+
Scalar scalar_value;
317+
if (result_value_sp->ResolveValue (scalar_value))
318+
{
319+
if (scalar_value.ULongLong(1) == 0)
320+
ret = false;
321+
else
322+
ret = true;
323+
if (log)
324+
log->Printf("Condition successfully evaluated, result is %s.\n",
325+
ret ? "true" : "false");
326+
}
327+
else
328+
{
329+
ret = false;
330+
error.SetErrorString("Failed to get an integer result from the expression");
331+
}
332+
}
333+
else
334+
{
335+
ret = false;
336+
error.SetErrorString("Failed to get any result from the expression");
337+
}
338+
}
339+
else
340+
{
341+
ret = false;
342+
error.SetErrorStringWithFormat("Couldn't execute expression:\n%s", execution_errors.GetData());
343+
}
344+
345+
return ret;
246346
}
247347

248348
uint32_t

lldb/source/Breakpoint/BreakpointOptions.cpp

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ BreakpointOptions::BreakpointOptions() :
4242
m_one_shot (false),
4343
m_ignore_count (0),
4444
m_thread_spec_ap (),
45-
m_condition_ap()
45+
m_condition_text (),
46+
m_condition_text_hash (0)
4647
{
4748
}
4849

@@ -56,13 +57,12 @@ BreakpointOptions::BreakpointOptions(const BreakpointOptions& rhs) :
5657
m_enabled (rhs.m_enabled),
5758
m_one_shot (rhs.m_one_shot),
5859
m_ignore_count (rhs.m_ignore_count),
59-
m_thread_spec_ap (),
60-
m_condition_ap ()
60+
m_thread_spec_ap ()
6161
{
6262
if (rhs.m_thread_spec_ap.get() != NULL)
6363
m_thread_spec_ap.reset (new ThreadSpec(*rhs.m_thread_spec_ap.get()));
64-
if (rhs.m_condition_ap.get())
65-
m_condition_ap.reset (new ClangUserExpression (rhs.m_condition_ap->GetUserText(), NULL, lldb::eLanguageTypeUnknown, ClangUserExpression::eResultTypeAny));
64+
m_condition_text = rhs.m_condition_text;
65+
m_condition_text_hash = rhs.m_condition_text_hash;
6666
}
6767

6868
//----------------------------------------------------------------------
@@ -79,8 +79,8 @@ BreakpointOptions::operator=(const BreakpointOptions& rhs)
7979
m_ignore_count = rhs.m_ignore_count;
8080
if (rhs.m_thread_spec_ap.get() != NULL)
8181
m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get()));
82-
if (rhs.m_condition_ap.get())
83-
m_condition_ap.reset (new ClangUserExpression (rhs.m_condition_ap->GetUserText(), NULL, lldb::eLanguageTypeUnknown, ClangUserExpression::eResultTypeAny));
82+
m_condition_text = rhs.m_condition_text;
83+
m_condition_text_hash = rhs.m_condition_text_hash;
8484
return *this;
8585
}
8686

@@ -162,24 +162,25 @@ BreakpointOptions::HasCallback ()
162162
void
163163
BreakpointOptions::SetCondition (const char *condition)
164164
{
165-
if (condition == NULL || condition[0] == '\0')
166-
{
167-
if (m_condition_ap.get())
168-
m_condition_ap.reset();
169-
}
170-
else
171-
{
172-
m_condition_ap.reset(new ClangUserExpression (condition, NULL, lldb::eLanguageTypeUnknown, ClangUserExpression::eResultTypeAny));
173-
}
165+
m_condition_text.assign(condition);
166+
std::hash<std::string> hasher;
167+
m_condition_text_hash = hasher(m_condition_text);
174168
}
175169

176170
const char *
177-
BreakpointOptions::GetConditionText () const
171+
BreakpointOptions::GetConditionText (size_t *hash) const
178172
{
179-
if (m_condition_ap.get())
180-
return m_condition_ap->GetUserText();
173+
if (!m_condition_text.empty())
174+
{
175+
if (hash)
176+
*hash = m_condition_text_hash;
177+
178+
return m_condition_text.c_str();
179+
}
181180
else
181+
{
182182
return NULL;
183+
}
183184
}
184185

185186
const ThreadSpec *
@@ -250,12 +251,12 @@ BreakpointOptions::GetDescription (Stream *s, lldb::DescriptionLevel level) cons
250251
m_callback_baton_sp->GetDescription (s, level);
251252
}
252253
}
253-
if (m_condition_ap.get())
254+
if (!m_condition_text.empty())
254255
{
255256
if (level != eDescriptionLevelBrief)
256257
{
257258
s->EOL();
258-
s->Printf("Condition: %s\n", m_condition_ap->GetUserText());
259+
s->Printf("Condition: %s\n", m_condition_text.c_str());
259260
}
260261
}
261262
}

lldb/source/Expression/ClangUserExpression.cpp

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,54 @@ ClangUserExpression::ScanContext(ExecutionContext &exe_ctx, Error &err)
329329
}
330330
}
331331

332+
void
333+
ClangUserExpression::InstallContext (ExecutionContext &exe_ctx)
334+
{
335+
m_process_wp = exe_ctx.GetProcessSP();
336+
337+
lldb::StackFrameSP frame_sp = exe_ctx.GetFrameSP();
338+
339+
if (frame_sp)
340+
m_address = frame_sp->GetFrameCodeAddress();
341+
}
342+
343+
bool
344+
ClangUserExpression::LockAndCheckContext (ExecutionContext &exe_ctx,
345+
lldb::TargetSP &target_sp,
346+
lldb::ProcessSP &process_sp,
347+
lldb::StackFrameSP &frame_sp)
348+
{
349+
lldb::ProcessSP expected_process_sp = m_process_wp.lock();
350+
process_sp = exe_ctx.GetProcessSP();
351+
352+
if (process_sp != expected_process_sp)
353+
return false;
354+
355+
process_sp = exe_ctx.GetProcessSP();
356+
target_sp = exe_ctx.GetTargetSP();
357+
frame_sp = exe_ctx.GetFrameSP();
358+
359+
if (m_address.IsValid())
360+
{
361+
if (!frame_sp)
362+
return false;
363+
else
364+
return (0 == Address::CompareLoadAddress(m_address, frame_sp->GetFrameCodeAddress(), target_sp.get()));
365+
}
366+
367+
return true;
368+
}
369+
370+
bool
371+
ClangUserExpression::MatchesContext (ExecutionContext &exe_ctx)
372+
{
373+
lldb::TargetSP target_sp;
374+
lldb::ProcessSP process_sp;
375+
lldb::StackFrameSP frame_sp;
376+
377+
return LockAndCheckContext(exe_ctx, target_sp, process_sp, frame_sp);
378+
}
379+
332380
// This is a really nasty hack, meant to fix Objective-C expressions of the form
333381
// (int)[myArray count]. Right now, because the type information for count is
334382
// not available, [myArray count] returns id, which can't be directly cast to
@@ -545,7 +593,7 @@ ClangUserExpression::PrepareToExecuteJITExpression (Stream &error_stream,
545593
process,
546594
frame))
547595
{
548-
error_stream.Printf("The context has changed before we could JIT the expression!");
596+
error_stream.Printf("The context has changed before we could JIT the expression!\n");
549597
return false;
550598
}
551599

0 commit comments

Comments
 (0)