Skip to content

Commit 185e133

Browse files
authored
Add support for LogPoints in OpenDebugAD7 (#1013)
* Add support for LogPoints in OpenDebugAD7 This PR adds LogMessage (Tracepoint) support for OpenDebugAD7. The messages with curl braces will have the expression inside of them evaluated. There is also support to replace tokens such as $PNAME,$TNAME,$TID,$CALLSTACK,$CALLER will have their values replaced.
1 parent 62b1e70 commit 185e133

File tree

8 files changed

+794
-21
lines changed

8 files changed

+794
-21
lines changed

src/OpenDebugAD7/AD7DebugSession.cs

Lines changed: 112 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ internal class AD7DebugSession : DebugAdapterBase, IDebugPortNotify2, IDebugEven
5454
private bool m_isAttach;
5555
private bool m_isCoreDump;
5656
private bool m_isStopped = false;
57+
private bool m_isStepping = false;
5758

5859
private readonly TaskCompletionSource<object> m_configurationDoneTCS = new TaskCompletionSource<object>();
5960

@@ -237,6 +238,31 @@ private ProtocolException VerifyProcessId(string processId, string telemetryEven
237238
return null;
238239
}
239240

241+
private IList<Tracepoint> GetTracepoints(IDebugBreakpointEvent2 debugEvent)
242+
{
243+
IList<Tracepoint> tracepoints = new List<Tracepoint>();
244+
245+
if (debugEvent != null)
246+
{
247+
debugEvent.EnumBreakpoints(out IEnumDebugBoundBreakpoints2 pBoundBreakpoints);
248+
IDebugBoundBreakpoint2[] boundBp = new IDebugBoundBreakpoint2[1];
249+
250+
uint numReturned = 0;
251+
while (pBoundBreakpoints.Next(1, boundBp, ref numReturned) == HRConstants.S_OK && numReturned == 1)
252+
{
253+
if (boundBp[0].GetPendingBreakpoint(out IDebugPendingBreakpoint2 ppPendingBreakpoint) == HRConstants.S_OK &&
254+
ppPendingBreakpoint.GetBreakpointRequest(out IDebugBreakpointRequest2 ppBPRequest) == HRConstants.S_OK &&
255+
ppBPRequest is AD7BreakPointRequest ad7BreakpointRequest &&
256+
ad7BreakpointRequest.HasTracepoint)
257+
{
258+
tracepoints.Add(ad7BreakpointRequest.Tracepoint);
259+
}
260+
}
261+
}
262+
263+
return tracepoints;
264+
}
265+
240266
#endregion
241267

242268
#region AD7EventHandlers helper methods
@@ -245,6 +271,7 @@ public void BeforeContinue()
245271
{
246272
if (!m_isCoreDump)
247273
{
274+
m_isStepping = false;
248275
m_isStopped = false;
249276
m_variableManager.Reset();
250277
m_frameHandles.Reset();
@@ -499,7 +526,6 @@ private void StepInternal(int threadId, enum_STEPKIND stepKind, enum_STEPUNIT st
499526
// If we are already running ignore additional step requests
500527
if (m_isStopped)
501528
{
502-
503529
IDebugThread2 thread = null;
504530
lock (m_threads)
505531
{
@@ -511,6 +537,7 @@ private void StepInternal(int threadId, enum_STEPKIND stepKind, enum_STEPUNIT st
511537

512538
BeforeContinue();
513539
ErrorBuilder builder = new ErrorBuilder(() => errorMessage);
540+
m_isStepping = true;
514541
try
515542
{
516543
builder.CheckHR(m_program.Step(thread, stepKind, stepUnit));
@@ -590,7 +617,8 @@ protected override void HandleInitializeRequestAsync(IRequestResponder<Initializ
590617
SupportsFunctionBreakpoints = m_engineConfiguration.FunctionBP,
591618
SupportsConditionalBreakpoints = m_engineConfiguration.ConditionalBP,
592619
ExceptionBreakpointFilters = m_engineConfiguration.ExceptionSettings.ExceptionBreakpointFilters.Select(item => new ExceptionBreakpointsFilter() { Default = item.@default, Filter = item.filter, Label = item.label }).ToList(),
593-
SupportsClipboardContext = m_engineConfiguration.ClipboardContext
620+
SupportsClipboardContext = m_engineConfiguration.ClipboardContext,
621+
SupportsLogPoints = true
594622
};
595623

596624
responder.SetResponse(initializeResponse);
@@ -1607,6 +1635,14 @@ protected override void HandleSetBreakpointsRequestAsync(IRequestResponder<SetBr
16071635
toRemove.Delete();
16081636
dict.Remove(bp.Line);
16091637
}
1638+
// Check to see if tracepoint changed
1639+
else if (!StringComparer.Ordinal.Equals(ad7BPRequest.LogMessage, bp.LogMessage))
1640+
{
1641+
ad7BPRequest.ClearTracepoint();
1642+
var toRemove = dict[bp.Line];
1643+
toRemove.Delete();
1644+
dict.Remove(bp.Line);
1645+
}
16101646
else
16111647
{
16121648
if (ad7BPRequest.BindResult != null)
@@ -1637,16 +1673,37 @@ protected override void HandleSetBreakpointsRequestAsync(IRequestResponder<SetBr
16371673

16381674
try
16391675
{
1640-
eb.CheckHR(m_engine.CreatePendingBreakpoint(pBPRequest, out pendingBp));
1641-
eb.CheckHR(pendingBp.Bind());
1676+
bool verified = true;
1677+
if (!string.IsNullOrEmpty(bp.LogMessage))
1678+
{
1679+
// Make sure tracepoint is valid.
1680+
verified = pBPRequest.SetLogMessage(bp.LogMessage);
1681+
}
16421682

1643-
dict[bp.Line] = pendingBp;
1644-
resBreakpoints.Add(new Breakpoint()
1683+
if (verified)
16451684
{
1646-
Id = (int)pBPRequest.Id,
1647-
Verified = true,
1648-
Line = bp.Line
1649-
});
1685+
eb.CheckHR(m_engine.CreatePendingBreakpoint(pBPRequest, out pendingBp));
1686+
eb.CheckHR(pendingBp.Bind());
1687+
1688+
dict[bp.Line] = pendingBp;
1689+
1690+
resBreakpoints.Add(new Breakpoint()
1691+
{
1692+
Id = (int)pBPRequest.Id,
1693+
Verified = verified,
1694+
Line = bp.Line
1695+
});
1696+
}
1697+
else
1698+
{
1699+
resBreakpoints.Add(new Breakpoint()
1700+
{
1701+
Id = (int)pBPRequest.Id,
1702+
Verified = verified,
1703+
Line = bp.Line,
1704+
Message = string.Format(CultureInfo.CurrentCulture, AD7Resources.Error_UnableToParseLogMessage)
1705+
});
1706+
}
16501707
}
16511708
catch (Exception e)
16521709
{
@@ -1893,12 +1950,10 @@ protected override void HandleEvaluateRequestAsync(IRequestResponder<EvaluateArg
18931950
hr = frame.GetExpressionContext(out expressionContext);
18941951
eb.CheckHR(hr);
18951952

1896-
const uint InputRadix = 10;
1897-
DAPEvalFlags dapEvalFlags = DAPEvalFlags.NONE;
18981953
IDebugExpression2 expressionObject;
18991954
string error;
19001955
uint errorIndex;
1901-
hr = expressionContext.ParseText(expression, enum_PARSEFLAGS.PARSE_EXPRESSION, InputRadix, out expressionObject, out error, out errorIndex);
1956+
hr = expressionContext.ParseText(expression, enum_PARSEFLAGS.PARSE_EXPRESSION, Constants.ParseRadix, out expressionObject, out error, out errorIndex);
19021957
if (!string.IsNullOrEmpty(error))
19031958
{
19041959
// TODO: Is this how errors should be returned?
@@ -1919,14 +1974,14 @@ protected override void HandleEvaluateRequestAsync(IRequestResponder<EvaluateArg
19191974
flags |= enum_EVALFLAGS.EVAL_NOSIDEEFFECTS;
19201975
}
19211976

1922-
if (context == EvaluateArguments.ContextValue.Clipboard)
1923-
{
1924-
dapEvalFlags |= DAPEvalFlags.CLIPBOARD_CONTEXT;
1925-
}
1926-
19271977
IDebugProperty2 property;
19281978
if (expressionObject is IDebugExpressionDAP expressionDapObject)
19291979
{
1980+
DAPEvalFlags dapEvalFlags = DAPEvalFlags.NONE;
1981+
if (context == EvaluateArguments.ContextValue.Clipboard)
1982+
{
1983+
dapEvalFlags |= DAPEvalFlags.CLIPBOARD_CONTEXT;
1984+
}
19301985
hr = expressionDapObject.EvaluateSync(flags, dapEvalFlags, Constants.EvaluationTimeout, null, out property);
19311986
}
19321987
else
@@ -2094,7 +2149,45 @@ public void HandleIDebugEntryPointEvent2(IDebugEngine2 pEngine, IDebugProcess2 p
20942149

20952150
public void HandleIDebugBreakpointEvent2(IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2 pProgram, IDebugThread2 pThread, IDebugEvent2 pEvent)
20962151
{
2097-
FireStoppedEvent(pThread, StoppedEvent.ReasonValue.Breakpoint);
2152+
IList<Tracepoint> tracepoints = GetTracepoints(pEvent as IDebugBreakpointEvent2);
2153+
if (tracepoints.Any())
2154+
{
2155+
ThreadPool.QueueUserWorkItem((o) =>
2156+
{
2157+
foreach (var tp in tracepoints)
2158+
{
2159+
int hr = tp.GetLogMessage(pThread, Constants.ParseRadix, m_processName, out string logMessage);
2160+
if (hr != HRConstants.S_OK)
2161+
{
2162+
DebuggerTelemetry.ReportError(DebuggerTelemetry.TelemetryTracepointEventName, logMessage);
2163+
m_logger.WriteLine(LoggingCategory.DebuggerError, logMessage);
2164+
}
2165+
else
2166+
{
2167+
m_logger.WriteLine(LoggingCategory.DebuggerStatus, logMessage);
2168+
}
2169+
}
2170+
2171+
// Need to check to see if the previous continuation of the debuggee was a step.
2172+
// If so, we need to send a stopping event to the UI to signal the step completed successfully.
2173+
if (!m_isStepping)
2174+
{
2175+
ThreadPool.QueueUserWorkItem((obj) =>
2176+
{
2177+
BeforeContinue();
2178+
m_program.Continue(pThread);
2179+
});
2180+
}
2181+
else
2182+
{
2183+
FireStoppedEvent(pThread, StoppedEvent.ReasonValue.Breakpoint);
2184+
}
2185+
});
2186+
}
2187+
else
2188+
{
2189+
FireStoppedEvent(pThread, StoppedEvent.ReasonValue.Breakpoint);
2190+
}
20982191
}
20992192

21002193
public void HandleIDebugBreakEvent2(IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2 pProgram, IDebugThread2 pThread, IDebugEvent2 pEvent)

src/OpenDebugAD7/AD7Impl/AD7BreakPointRequest.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,5 +112,40 @@ public int GetChecksum(ref Guid guidAlgorithm, CHECKSUM_DATA[] checksumData)
112112
return HRConstants.E_FAIL;
113113
}
114114
}
115+
116+
#region Tracepoints
117+
118+
private string m_logMessage;
119+
private Tracepoint m_Tracepoint;
120+
121+
public void ClearTracepoint()
122+
{
123+
m_logMessage = null;
124+
m_Tracepoint = null;
125+
}
126+
127+
public bool SetLogMessage(string logMessage)
128+
{
129+
try
130+
{
131+
m_Tracepoint = Tracepoint.CreateTracepoint(logMessage);
132+
DebuggerTelemetry.ReportEvent(DebuggerTelemetry.TelemetryTracepointEventName);
133+
}
134+
catch (InvalidTracepointException e)
135+
{
136+
DebuggerTelemetry.ReportError(DebuggerTelemetry.TelemetryTracepointEventName, e.Message);
137+
return false;
138+
}
139+
m_logMessage = logMessage;
140+
return true;
141+
}
142+
143+
public string LogMessage => m_logMessage;
144+
145+
public bool HasTracepoint => !string.IsNullOrEmpty(m_logMessage) && m_Tracepoint != null;
146+
147+
public Tracepoint Tracepoint => m_Tracepoint;
148+
149+
#endregion
115150
}
116151
}

src/OpenDebugAD7/AD7Resources.Designer.cs

Lines changed: 72 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/OpenDebugAD7/AD7Resources.resx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@
192192
<data name="Error_UnableToSetBreakpoint" xml:space="preserve">
193193
<value>Error setting breakpoint. {0}</value>
194194
</data>
195+
<data name="Error_UnableToParseLogMessage" xml:space="preserve">
196+
<value>Unable to parse 'logMessage'.</value>
197+
</data>
195198
<data name="Msg_E_CRASHDUMP_UNSUPPORTED" xml:space="preserve">
196199
<value>This operation is not supported when debugging dump files.</value>
197200
</data>
@@ -258,4 +261,25 @@
258261
<value>Exception occurred: '{0}'</value>
259262
<comment>{0} is the exception string</comment>
260263
</data>
264+
<data name="Error_InterpolateMissingFrames" xml:space="preserve">
265+
<value>Unable to interpolate logMessage because frames could not be retrieved.</value>
266+
</data>
267+
<data name="Error_InterpolateMissingThread" xml:space="preserve">
268+
<value>Unable to interpolate logMessage because current thread is missing.</value>
269+
</data>
270+
<data name="Error_InterpolateMissingTopFrame" xml:space="preserve">
271+
<value>Unable to interpolate logMessage because there is no top frame.</value>
272+
</data>
273+
<data name="Error_InterpolateVariableEvaluateFailed" xml:space="preserve">
274+
<value>Failed to evaluate expression.</value>
275+
</data>
276+
<data name="Error_InterpolateVariableMissingContext" xml:space="preserve">
277+
<value>Unable to get context from frame.</value>
278+
</data>
279+
<data name="Error_InterpolateVariableMissingExpressionObject" xml:space="preserve">
280+
<value>No expression object found.</value>
281+
</data>
282+
<data name="Error_InterpolateVariableMissingProperties" xml:space="preserve">
283+
<value>Failed to get property information.</value>
284+
</data>
261285
</root>

src/OpenDebugAD7/Constants.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ internal static class Constants
2828
{
2929
// POST_PREVIEW_TODO: no-func-eval support, radix, timeout
3030
public const uint EvaluationRadix = 10;
31+
public const uint ParseRadix = 10;
3132
public const uint EvaluationTimeout = 5000;
3233
public const int DisconnectTimeout = 2000;
34+
public const int DefaultTracepointCallstackDepth = 10;
3335
}
34-
}
36+
}

src/OpenDebugAD7/OpenDebugAD7.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@
147147
<Compile Include="Telemetry\TelemetryHelper.cs" />
148148
<Compile Include="TextPositionTuple.cs" />
149149
<Compile Include="ThreadFrameEnumInfo.cs" />
150+
<Compile Include="Tracepoint.cs" />
150151
<Compile Include="VariableManager.cs" />
151152
<Compile Include="AD7DebugSession.cs" />
152153
</ItemGroup>

0 commit comments

Comments
 (0)