
//The procedures outputting the call stack chain specifically ignore procedures from this include, as well as mo_hub.die.inc

var
  _crash_details: WideString;

  function CallStack(IgnoreLastN: integer; Padding: WideString): WideString;
  var
    a, ca: pointer; i: integer;
    w: WideString;
  begin
    Result:='';
    i:=0;
    a:= get_frame;
    while Assigned(a) do begin
      ca:= get_caller_addr(a);
      if Assigned(ca) then begin
        w:= ShortExpAddr(ca);
        if ((IgnoreLastN > 0) and (i >= IgnoreLastN + 1))
        or ((IgnoreLastN = 0) and (pos('mo_hub_die.inc', w) = 0) and (pos('cl_die.inc', w) = 0) and (pos('mo_die1.inc', w) = 0) and (pos('cl_seh_hack.pp', w) = 0))
        then begin
          if Result <> '' then Result+= #10#13;
          Result+= Padding + w;
        end;
        inc(i);
      end
      else Break;
      a:= get_caller_frame(a);
    end;
  end;


 Procedure Die(AnsiYell: AnsiString);
  begin
    Die(AnsiToWide(AnsiYell));
  end;


  Procedure Die(AnsiYell :AnsiString; Param: array of const); OVERLOAD;
  begin
    Die(PervertedFormat(AnsitoWide(AnsiYell), Param));
  end;

  Procedure Die(WideYell :WideString; Param: array of const); OVERLOAD;
  begin
    Die(PervertedFormat(WideYell, Param));
  end;


  Procedure Die(Ru, En :WideString; Param: array of const); OVERLOAD;
  begin
    Die(RuEn(Ru,En), Param);
  end;

  Procedure Die(YellID :TMessageID);
  begin
    Die(YellId, []);
  end;



  Procedure Die(YellID :TMessageID; Param: array of const);
  var
    U: WideString;
    Ey: AnsiString;
  begin
    U:=PervertedFormat(MessageContainer[YellID], Param);
    if not MotherState.NowDying then begin
      CheckForGenericDyingYells(U);
      AddLog(MI_SHIT_HAPPENS_SEE_BELOW, ['----']);
      with MotherState do
        if not DeveloperMode and not DebugMode
          then AddLog('%0'#10#13'<---------------------->',[_crash_Details]);
      MotherState.NowDying:=Yes;
    end;
    Ey:=WideToAnsi(U);
    WarningQueue.Add(U);
    raise Exception.Create(ey);
  end;

  Procedure Die(WideYell: WideString);
  begin
    if not MotherState.NowDying then begin
      CheckForGenericDyingYells(WideYell);
      AddLog(MI_SHIT_HAPPENS_SEE_BELOW, ['----']);
      with MotherState do
        if not DeveloperMode and not DebugMode
          then AddLog('%0'#10#13'<---------------------->',[_crash_Details]);
      MotherState.NowDying:=Yes;
    end;
    WarningQueue.Add(WideYell);
    raise Exception.Create('');
  end;

  procedure ProcessGuardedException; cdecl;
  var
    CallerAddr: pointer;
    Yell: WideString;
  begin
    asm
     {$ifdef cpu64}
      mov rax, [rbp + 8]
      mov [CallerAddr], rax
     {$else}
      mov eax, [ebp + 4] //read the caller's EIP
      mov [CallerAddr], eax
     {$endif}
    end;
    if CurrentOwner = NOT_A_MODULE
    then begin
      if MotherState.NowDying
      then raise Exception.Create('')
      else Die(RuEn(
        'Исключение охраняемой функции'#10#13'  отловлено шлюзом %0',
        'The guarded function exception'#10#13'  re-raised at %0'),
        [ExpExpAddress(CallerAddr)])
    end
    else begin
      if not MotherState.NowDying then begin
        inExportedproc:=true;
        Yell:=PervertedFormat(RuEn(
          'Исключение коллбэк-функции модуля-матки'#10#13'  передано шлюзом %0',
          'The mother module callback function exception'#10#13'  send at %0'),
          [ExpExpAddress(CallerAddr)]);
        CheckForGenericDyingYells(Yell, 0);
        AddYell(Yell);
        MotherState.NowDying:=Yes;
      end;
      inExportedproc:=false;
    end;
  end;


  procedure CheckForGenericDyingYells(var U: WideString; omit_frames: integer = 0);
  var
    a, ca: pointer;
    i: integer;
    csstarted: boolean = false;
    w: WideString;
  begin
    if MotherState.NowDying then Exit;
    //Try
  //      SetEChar;
      if ExceptObjectisException then begin
        if not (ExceptObject is EFake) then begin
          U+= #10#13#10#13 + TellException(ExceptObject as Exception);
          MotherState.DyingAfterTrueException:= Yes;
        {$ifdef win32}
          //..пробуем расшифровать показания
          //  функции WinAPI GetLastEror (в функции Win32LastEror)
          U+= Win32LastError;
        {$endif}
        end;
      end;
      {$ifdef win32}
        SetLastError(0);
      {$endif}
      _crash_details:= '';
      if not MotherState.CallStackLogged then begin
        i:=0;
        a:= get_frame;
        while Assigned(a) do begin
          ca:= get_caller_addr(a);
          if Assigned(ca) then begin
            w:= ShortExpAddr(ca);
            if (pos('mo_hub_die.inc', w) = 0) and (pos('cl_die.inc', w) = 0) and (pos('mo_die1.inc', w) = 0) and (pos('cl_seh_hack.pp', w) = 0) then begin
              if not csstarted then begin
                csstarted:= true;
                _crash_details+= #10#13'Call stack:';
              end;
              _crash_details+= #10#13'  ' + w;
            end;
            inc(i);
          end
          else Break;
          if (i>MAX_STACK_FRAMES_DUMP)
            and not MotherState.DebugMode
            and MotherState.ProgramInitialized then break;
          a:= get_caller_frame(a);
        end;
        with MotherState do
          if DeveloperMode or DebugMode or not ProgramInitialized
            then U+= #10#13#10#13 + _crash_Details;
        MotherState.CallStackLogged:= true;
      end;
    //Except
    //  U:=U + #10#13'!Crashed accessing Exception object!';
    //End;
  end;

