microsoft / ConcordExtensibilitySamples

Visual Studio Debug Engine Extensibility Samples
Other
121 stars 50 forks source link

Custom breakpoints, inactive when set before module loads #114

Open Juliean opened 5 months ago

Juliean commented 5 months ago

I'm having a problem with the breakpoints that my extension handles, for my JIT-compiled language. I've implemented IDkmSymbolDocumentCollectionQuery and IDkmSymbolDocumentSpanQuery, and all breakpoints that I set in my source-file will call into those. They activate correctly, and IDkmRuntimeMonitorBreakpointHandler.EnableRuntimeBreakpoint on my server-component is called for them.

However, whenever a breakpoint is set before a module is loaded for the according source-file, the whole file get's stuck in a "no symbols loaded" state for all breakpoints - even those that I create after the module is loaded (modules-window confirms this). Only if I remove all breakpoints that show "no symbols loaded", can I set a proper working breakpoint again.

From debugging, it seems that IDkmSymbolDocumentCollectionQuery will not be invoked at all, in the mentioned state - only after the last breakpoint is removed, will the next set breakpoint trigger the query. grafik

From playing around with diverse interfaces, it seems that IDkmPendingFileLineBreakpointCallback gets called for any such breakpoint at app startup, with null-guids for compiler/language. So maybe this causes the debugger to confuse the source for something else? Not sure how I'd fix it though. My code is roughly based on PVTS.

My code for the two queries is:

DkmResolvedDocument[] IDkmSymbolDocumentCollectionQuery.FindDocuments(DkmModule module, DkmSourceFileId sourceFileId)
{
    DkmDocumentMatchStrength matchStrength;

    var moduleName = Path.GetFileName(Path.GetFileNameWithoutExtension(module.Name));
    var sourceModuleName = Path.GetDirectoryName(sourceFileId.DocumentName);
    sourceModuleName = Path.GetFileName(sourceModuleName.TrimEnd('\\'));

    if (string.Equals(moduleName, sourceModuleName, StringComparison.OrdinalIgnoreCase))
    {
        matchStrength = DkmDocumentMatchStrength.FileName;
        return new[] {
            DkmResolvedDocument.Create(module, sourceFileId.DocumentName, null, matchStrength, DkmResolvedDocumentWarning.None, false, null)
        };
    }
    else
        return new DkmResolvedDocument[0];
}

DkmInstructionSymbol[] IDkmSymbolDocumentSpanQuery.FindSymbols(DkmResolvedDocument resolvedDocument, DkmTextSpan textSpan, string text, out DkmSourcePosition[] symbolLocation)
{
    var sourceFileId = DkmSourceFileId.Create(resolvedDocument.DocumentName, null, null, null);
    var resultSpan = new DkmTextSpan(textSpan.StartLine, textSpan.StartLine, 0, 0);
    symbolLocation = new[] { DkmSourcePosition.Create(sourceFileId, resultSpan) };

    var location = new SourceLocation(string.Empty, string.Empty, 0, resolvedDocument.DocumentName, textSpan.StartLine);
    var encodedLocation = location.Encode();

    return new[] { DkmCustomInstructionSymbol.Create(resolvedDocument.Module, Guids.AcclimateRuntimeTypeGuid, encodedLocation, 0, encodedLocation) };
}

Line/File-information is correct, it's also used to display IDkmSymbolQuery.GetSourcePosition correctly. Modules and its symbols are customized, created similar to PVTS as well:

var fileName = Path.ChangeExtension(FileName, ".dbg");

var module = DkmModule.Create(new DkmModuleId(ModuleId, Guids.AcclimateSymbolProviderGuid), fileName,
    new DkmCompilerId(Guids.EnvileMediaVendorGuid, Guids.AcclimateLanguageGuid), process.Connection, null, fileName, null);
var moduleInstance = DkmCustomModuleInstance.Create(ModuleName, fileName, Size, eventRuntime, null, null, DkmModuleFlags.None,
    DkmModuleMemoryLayout.Unknown, BaseAddress, 0, Size, "AEEvent", false, null, null, null);
moduleInstance.SetModule(module, true);

Is ther anything else I'd need to implement to ensure that module-load actually triggers a breakpoint/source-file symbol refresh? Or something to supply symbols for source-files outside of the two aformentioned queries?