internal static DkmEvaluationResult GetLuaFunctionChildAtIndex(DkmInspectionContext inspectionContext, DkmStackWalkFrame stackFrame, string fullName, LuaClosureData value, int index) { var process = stackFrame.Process; var processData = DebugHelpers.GetOrCreateDataItem <LuaLocalProcessData>(process); if (index == 0) { if (value == null) { return(DkmFailedEvaluationResult.Create(inspectionContext, stackFrame, "[function]", $"{fullName}.!function", "null", DkmEvaluationResultFlags.Invalid, null)); } var functionData = value.ReadFunction(process); if (functionData == null) { return(DkmFailedEvaluationResult.Create(inspectionContext, stackFrame, "[function]", $"{fullName}.!function", "[internal error: failed to read Proto]", DkmEvaluationResultFlags.Invalid, null)); } string source = functionData.ReadSource(process); if (source == null) { return(DkmFailedEvaluationResult.Create(inspectionContext, stackFrame, "[function]", $"{fullName}.!function", "[internal error: failed to read source]", DkmEvaluationResultFlags.Invalid, null)); } int line = functionData.definitionStartLine_opt; DkmEvaluationResultCategory category = DkmEvaluationResultCategory.Method; DkmEvaluationResultTypeModifierFlags typeModifiers = DkmEvaluationResultTypeModifierFlags.None; DkmEvaluationResultAccessType access = DkmEvaluationResultAccessType.Public; DkmEvaluationResultStorageType storage = DkmEvaluationResultStorageType.Global; LuaAddressEntityData entityData = new LuaAddressEntityData { source = source, line = line, functionAddress = 0, functionInstructionPointer = 0, }; var entityDataBytes = entityData.Encode(); DkmInstructionAddress instructionAddress = DkmCustomInstructionAddress.Create(processData.runtimeInstance, processData.moduleInstance, entityDataBytes, (ulong)((line << 16) + 0), null, null); DkmDataAddress dataAddress = DkmDataAddress.Create(processData.runtimeInstance, value.functionAddress, instructionAddress); return(DkmSuccessEvaluationResult.Create(inspectionContext, stackFrame, "[function]", $"{fullName}.!function", DkmEvaluationResultFlags.ReadOnly | DkmEvaluationResultFlags.Address, $"{source}:{line}", null, "Proto*", category, access, storage, typeModifiers, dataAddress, null, null, null)); } Debug.Assert(false, "Invalid child index"); return(null); }
void IDkmLanguageConditionEvaluator.EvaluateCondition(DkmEvaluationBreakpointCondition evaluationCondition, DkmStackWalkFrame stackFrame, out bool stop, out string errorText) { DkmProcess process = stackFrame.Process; var processData = DebugHelpers.GetOrCreateDataItem <LuaRemoteProcessData>(process); if (processData.locations == null) { stop = true; errorText = "Debug helper data for conditional breakpoint is missing"; return; } DkmInspectionSession inspectionSession = DkmInspectionSession.Create(process, null); ulong stateAddress = DebugHelpers.ReadPointerVariable(process, processData.locations.helperBreakHitLuaStateAddress).GetValueOrDefault(0); if (stateAddress == 0) { inspectionSession.Close(); stop = true; errorText = "Failed to evaluate current Lua state address"; return; } ulong callInfoAddress = 0; // Read lua_State ulong temp = stateAddress; ulong?savedProgramCounterAddress = null; // CommonHeader DebugHelpers.SkipStructPointer(process, ref temp); DebugHelpers.SkipStructByte(process, ref temp); DebugHelpers.SkipStructByte(process, ref temp); if (processData.luaVersion == 501) { DebugHelpers.SkipStructByte(process, ref temp); // status DebugHelpers.SkipStructPointer(process, ref temp); // top DebugHelpers.SkipStructPointer(process, ref temp); // base DebugHelpers.SkipStructPointer(process, ref temp); // l_G callInfoAddress = DebugHelpers.ReadStructPointer(process, ref temp).GetValueOrDefault(0); savedProgramCounterAddress = DebugHelpers.ReadStructPointer(process, ref temp).GetValueOrDefault(0); } else if (processData.luaVersion == 502) { DebugHelpers.SkipStructByte(process, ref temp); // status DebugHelpers.SkipStructPointer(process, ref temp); // top DebugHelpers.SkipStructPointer(process, ref temp); // l_G callInfoAddress = DebugHelpers.ReadStructPointer(process, ref temp).GetValueOrDefault(0); } else if (processData.luaVersion == 503) { DebugHelpers.SkipStructShort(process, ref temp); // nci DebugHelpers.SkipStructByte(process, ref temp); // status DebugHelpers.SkipStructPointer(process, ref temp); // top DebugHelpers.SkipStructPointer(process, ref temp); // l_G callInfoAddress = DebugHelpers.ReadStructPointer(process, ref temp).GetValueOrDefault(0); } else if (processData.luaVersion == 504) { DebugHelpers.SkipStructByte(process, ref temp); // status DebugHelpers.SkipStructByte(process, ref temp); // allowhook DebugHelpers.SkipStructShort(process, ref temp); // nci DebugHelpers.SkipStructPointer(process, ref temp); // top DebugHelpers.SkipStructPointer(process, ref temp); // l_G callInfoAddress = DebugHelpers.ReadStructPointer(process, ref temp).GetValueOrDefault(0); } if (callInfoAddress == 0) { inspectionSession.Close(); stop = true; errorText = $"Failed to evaluate current Lua call frame (Lua version {processData.luaVersion})"; return; } // Load call info data (to get base stack address) LuaFunctionCallInfoData callInfoData = new LuaFunctionCallInfoData(); callInfoData.ReadFrom(process, callInfoAddress); // TODO: cache? callInfoData.ReadFunction(process); if (callInfoData.func == null) { inspectionSession.Close(); stop = true; errorText = $"Failed to evaluate current Lua call frame function (Lua version {processData.luaVersion})"; return; } if (callInfoData.func.extendedType != LuaExtendedType.LuaFunction) { inspectionSession.Close(); stop = true; errorText = "Breakpoint location has to be inside a Lua function"; return; } LuaValueDataLuaFunction currCallLuaFunction = callInfoData.func as LuaValueDataLuaFunction; LuaClosureData closureData = currCallLuaFunction.value; LuaFunctionData functionData = null; if (processData.functionDataCache.ContainsKey(closureData.functionAddress)) { functionData = processData.functionDataCache[closureData.functionAddress]; } else { functionData = closureData.ReadFunction(process); functionData.ReadUpvalues(process); functionData.ReadLocals(process, -1); processData.functionDataCache.Add(closureData.functionAddress, functionData); } if (!savedProgramCounterAddress.HasValue) { savedProgramCounterAddress = callInfoData.savedInstructionPointerAddress; } // Possible in bad break locations if (savedProgramCounterAddress < functionData.codeDataAddress) { inspectionSession.Close(); stop = true; errorText = "Invalid saved program counter"; return; } long currInstructionPointer = ((long)savedProgramCounterAddress - (long)functionData.codeDataAddress) / 4; // unsigned size instructions // If the call was already made, savedpc will be offset by 1 (return location) int prevInstructionPointer = currInstructionPointer == 0 ? 0 : (int)currInstructionPointer - 1; functionData.UpdateLocals(process, prevInstructionPointer); ExpressionEvaluation evaluation = new ExpressionEvaluation(process, functionData, callInfoData.stackBaseAddress, closureData); var result = evaluation.Evaluate(evaluationCondition.Source.Text); if (result as LuaValueDataError != null) { var resultAsError = result as LuaValueDataError; inspectionSession.Close(); stop = true; errorText = resultAsError.value; return; } if (result.baseType == LuaBaseType.Nil) { inspectionSession.Close(); stop = false; errorText = null; return; } if (result is LuaValueDataBool resultBool) { inspectionSession.Close(); stop = resultBool.value; errorText = null; return; } var resultNumber = result as LuaValueDataNumber; if (resultNumber == null) { inspectionSession.Close(); stop = true; errorText = $"Value can't be used as condition: {result.AsSimpleDisplayString(10)}"; return; } inspectionSession.Close(); stop = resultNumber.value != 0.0; errorText = null; }