void ClearStepperData(DkmProcess process, LuaRemoteProcessData processData)
        {
            if (processData.locations != null)
            {
                DebugHelpers.TryWriteIntVariable(process, processData.locations.helperStepOverAddress, 0);
                DebugHelpers.TryWriteIntVariable(process, processData.locations.helperStepIntoAddress, 0);
                DebugHelpers.TryWriteIntVariable(process, processData.locations.helperStepOutAddress, 0);
                DebugHelpers.TryWriteIntVariable(process, processData.locations.helperSkipDepthAddress, 0);
            }

            processData.activeStepper = null;
        }
        void UpdateBreakpoints(DkmProcess process, LuaRemoteProcessData processData)
        {
            // Can't update breakpoints if we don't have the hook attached
            if (processData.locations == null)
            {
                return;
            }

            UpdateHooks(process, processData);

            int count = processData.activeBreakpoints.Count;

            if (count > 256)
            {
                count = 256;
            }

            ulong pointerSize = (ulong)DebugHelpers.GetPointerSize(process);

            ulong sourceNameAddress = processData.locations.helperBreakSourcesAddress;

            for (int i = 0; i < count; i++)
            {
                ulong dataAddress = processData.locations.helperBreakDataAddress + (ulong)i * 3 * pointerSize;

                var breakpoint = processData.activeBreakpoints[i];

                DebugHelpers.TryWritePointerVariable(process, dataAddress, (ulong)breakpoint.line);

                if (breakpoint.functionAddress == 0)
                {
                    Debug.Assert(breakpoint.source != null);

                    byte[] sourceNameBytes = Encoding.UTF8.GetBytes(breakpoint.source);

                    DebugHelpers.TryWriteRawBytes(process, sourceNameAddress, sourceNameBytes);
                    DebugHelpers.TryWriteByteVariable(process, sourceNameAddress + (ulong)sourceNameBytes.Length, 0);

                    ulong currSourceNameAddress = sourceNameAddress;
                    sourceNameAddress += (ulong)sourceNameBytes.Length + 1;

                    DebugHelpers.TryWritePointerVariable(process, dataAddress + pointerSize, 0);
                    DebugHelpers.TryWritePointerVariable(process, dataAddress + pointerSize * 2, currSourceNameAddress);
                }
                else
                {
                    DebugHelpers.TryWritePointerVariable(process, dataAddress + pointerSize, breakpoint.functionAddress);
                    DebugHelpers.TryWritePointerVariable(process, dataAddress + pointerSize * 2, 0);
                }
            }

            DebugHelpers.TryWriteIntVariable(process, processData.locations.helperBreakCountAddress, count);
        }
        void SetupHooks(DkmProcess process, LuaRemoteProcessData processData)
        {
            processData.hooksEnabled = true;

            foreach (var stateKV in processData.knownStates)
            {
                var state = stateKV.Value;

                DebugHelpers.TryWritePointerVariable(process, state.hookFunctionAddress, state.helperHookFunctionAddress);

                if (processData.luaVersion == 503 || processData.luaVersion == 504)
                {
                    DebugHelpers.TryWriteIntVariable(process, state.hookMaskAddress, 7); // LUA_HOOKLINE | LUA_HOOKCALL | LUA_HOOKRET
                }
                else
                {
                    DebugHelpers.TryWriteByteVariable(process, state.hookMaskAddress, 7); // LUA_HOOKLINE | LUA_HOOKCALL | LUA_HOOKRET
                }
                DebugHelpers.TryWriteIntVariable(process, state.hookBaseCountAddress, 0);
                DebugHelpers.TryWriteIntVariable(process, state.hookCountAddress, 0);

                // Lua 5.4 has to update 'trap' flag for all Lua call stack frames
                if (processData.luaVersion == 504)
                {
                    ulong?callInfo = DebugHelpers.ReadPointerVariable(process, state.stateAddress + state.setTrapStateCallInfoOffset);

                    while (callInfo.HasValue && callInfo.Value != 0)
                    {
                        var callStatus = DebugHelpers.ReadShortVariable(process, callInfo.Value + state.setTrapCallInfoCallStatusOffset);

                        if (callStatus.HasValue && (callStatus.Value & (int)CallStatus_5_4.C) == 0)
                        {
                            if (!DebugHelpers.TryWriteIntVariable(process, callInfo.Value + state.setTrapCallInfoTrapOffset, 1))
                            {
                                break;
                            }
                        }

                        callInfo = DebugHelpers.ReadPointerVariable(process, callInfo.Value + state.setTrapCallInfoPreviousOffset);
                    }
                }
            }
        }
        void RemoveHooks(DkmProcess process, LuaRemoteProcessData processData)
        {
            processData.hooksEnabled = false;

            foreach (var stateKV in processData.knownStates)
            {
                var state = stateKV.Value;

                DebugHelpers.TryWritePointerVariable(process, state.hookFunctionAddress, 0);

                if (processData.luaVersion == 503 || processData.luaVersion == 504)
                {
                    DebugHelpers.TryWriteIntVariable(process, state.hookMaskAddress, 0);
                }
                else
                {
                    DebugHelpers.TryWriteByteVariable(process, state.hookMaskAddress, 0);
                }

                DebugHelpers.TryWriteIntVariable(process, state.hookBaseCountAddress, 0);
                DebugHelpers.TryWriteIntVariable(process, state.hookCountAddress, 0);
            }
        }
        void IDkmRuntimeStepper.Step(DkmRuntimeInstance runtimeInstance, DkmStepper stepper, DkmStepArbitrationReason reason)
        {
            var process = runtimeInstance.Process;

            var processData = DebugHelpers.GetOrCreateDataItem <LuaRemoteProcessData>(runtimeInstance.Process);

            if (stepper.StepKind == DkmStepKind.StepIntoSpecific)
            {
                throw new NotSupportedException();
            }

            if (processData.activeStepper != null)
            {
                processData.activeStepper.CancelStepper(processData.runtimeInstance);
                processData.activeStepper = null;
            }

            if (stepper.StepKind == DkmStepKind.Over)
            {
                DebugHelpers.TryWriteIntVariable(process, processData.locations.helperStepOverAddress, 1);
            }
            else if (stepper.StepKind == DkmStepKind.Into)
            {
                DebugHelpers.TryWriteIntVariable(process, processData.locations.helperStepOverAddress, 1);
                DebugHelpers.TryWriteIntVariable(process, processData.locations.helperStepIntoAddress, 1);
            }
            else if (stepper.StepKind == DkmStepKind.Out)
            {
                DebugHelpers.TryWriteIntVariable(process, processData.locations.helperStepOutAddress, 1);
            }

            processData.activeStepper = stepper;

            processData.hadActiveStepper = true;

            UpdateHooks(process, processData);
        }