Getting a "frozen" bug report for a secondary exe file

delphi package - automated exception handling
Post Reply
obones
Posts: 66
Joined: Fri May 15, 2009 11:47 am

Getting a "frozen" bug report for a secondary exe file

Post by obones »

Hello,

I have a main application (Main.exe) that calls CreateProcess to start a secondary process (Child.exe) and waits for it to finish. Both exe files are embedding madExcept.
In some rare instances, this deadlocks and I have come up with the following instrumentation method inside Main.exe:

Create a watchdog thread
Get stack traces for all threads
Compare with previously saved stack traces, if equal, increase counter
If counter is above threshold, call NewException(etFrozen, nil, nil, False), dump the report and stop the application.

This works well in preventing the application to run "forever", but it only tells me that Main.exe is stuck waiting for Child.exe
To really figure out what's going on, I would need the same report dump for Child.exe and so was wondering if I could send a "signal" to that exe that madExcept would automatically intercept and thus create a bugreport file. Is there such a mechanism in place inside madExcept?

I know how to implement this, but if it's already there, it would be better to use it.
madshi
Site Admin
Posts: 10753
Joined: Sun Mar 21, 2004 5:25 pm

Re: Getting a "frozen" bug report for a secondary exe file

Post by madshi »

It's kinda already there. See madTraceProcess. Source code should be included. It has some dependencies, though, but you can probably drop most of them. E.g. you can replace CreateRemoteThreadEx with CreateRemoteThread.
obones
Posts: 66
Joined: Fri May 15, 2009 11:47 am

Re: Getting a "frozen" bug report for a secondary exe file

Post by obones »

Thanks for the hint, I did not know this existed.
I managed to adapt it into a function that gets a Process Id and returns a string, it's all I needed and it works just fine. Do you want me to post it somewhere?

With that I can see what the child process is doing, which is looping on Application.Idle, which is weird, but there's nothing madExcept can do about that. Back to the drawing board, but at least now, I can inspect all the puzzle pieces.
madshi
Site Admin
Posts: 10753
Joined: Sun Mar 21, 2004 5:25 pm

Re: Getting a "frozen" bug report for a secondary exe file

Post by madshi »

How long is that function? If it's not too long, I suppose you could paste it into a "code" block here, in case it might be helpful to other users.
obones
Posts: 66
Joined: Fri May 15, 2009 11:47 am

Re: Getting a "frozen" bug report for a secondary exe file

Post by obones »

I was waiting for your validation as this is based on your own code. So here it goes:

Code: Select all

function GetProcessBugReport(ProcessId: DWORD): string;
var
  SecurityAttributes: TSecurityAttributes;
  SecurityDescriptor: TSecurityDescriptor;
  ProcessHandle: THandle;
  TraceFunction: Pointer;
  {$ifdef win64}
  SessionId: DWORD;
  {$endif}
  I, J: Integer;
  FileMappingHandle: THandle;
  C: DWORD;
  RemoteString: AnsiString;
  Buffer: Pointer;
begin
  SecurityAttributes.nLength := SizeOf(SecurityAttributes);
  SecurityAttributes.lpSecurityDescriptor := @SecurityDescriptor;
  SecurityAttributes.bInheritHandle := False;
  InitializeSecurityDescriptor(@SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
  SetSecurityDescriptorDacl(@SecurityDescriptor, True, nil, False);
  ProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, False, ProcessId);
  if ProcessHandle <> 0 then
  begin
    try
      TraceFunction := nil;
      {$ifdef win64}
        FileMappingHandle := OpenFileMappingA(FILE_MAP_READ, False, PAnsiChar('madTraceProcess' + IntToHexEx(ProcessId)));
        if (FileMappingHandle = 0) and (GetLastError <> 5) then
        begin
          ProcessIdToSessionId(ProcessId, SessionId);
          FileMappingHandle := OpenFileMappingA(FILE_MAP_READ, False, PAnsiChar('Session\' + IntToStrEx(SessionId) + '\madTraceProcess' + IntToHexEx(ProcessId)));
        end;

        if FileMappingHandle <> 0 then
        begin
          Buffer := MapViewOfFile(FileMappingHandle, FILE_MAP_READ, 0, 0, 0);
          if Buffer <> nil then
          begin
            TraceFunction := pointer(Buffer^);
            UnmapViewOfFile(Buffer);
          end;
          CloseHandle(FileMappingHandle);
        end;
      {$else}
        with Process(ProcessHandle).ExportList do
          if ItemCount = 0 then
          begin
            Exit('Opening the process failed. Try running as admin.');
          end
          else
          begin
            for I := 0 to ItemCount - 1 do
              if Items[I].Name = 'madTraceProcess' then
              begin
                TraceFunction := Items[I].Address;
                break;
              end;
          end;
      {$endif}
      if TraceFunction <> nil then
      begin
        EmptyClipboard;
        if GetVersion and $80000000 = 0 then
        begin
          FileMappingHandle := CreateFileMapping(INVALID_HANDLE_VALUE, @SecurityAttributes, PAGE_READWRITE, 0, $100000, 'Global\madTraceProcessMap');
          if FileMappingHandle = 0 then
            FileMappingHandle := CreateFileMapping(INVALID_HANDLE_VALUE, @SecurityAttributes, PAGE_READWRITE, 0, $100000, 'madTraceProcessMap');
        end
        else
        begin
          FileMappingHandle := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, $100000, 'madTraceProcessMap');
        end;

        try
          if FileMappingHandle <> 0 then
            C := $100000
          else
            C := 0;
          C := CreateRemoteThread(ProcessHandle, @SecurityAttributes, 0, TraceFunction, pointer(C), CREATE_SUSPENDED, C);
          if C <> 0 then
          begin
            SetThreadPriority(C, THREAD_PRIORITY_TIME_CRITICAL);
            ResumeThread(C);
            WaitForSingleObject(C, INFINITE);
            CloseHandle(C);
            RemoteString := '';
            if FileMappingHandle <> 0 then
            begin
              Buffer := MapViewOfFile(FileMappingHandle, FILE_MAP_READ, 0, 0, 0);
              if Buffer <> nil then
              begin
                RemoteString := PAnsiChar(Buffer);
                UnmapViewOfFile(Buffer);
              end;
            end;
            if RemoteString = '' then
              try
                RemoteString := AnsiString(Clipboard.AsText);
              except
              end;
            I := PosText(AnsiString('exception number'), RemoteString);
            if I > 0 then
            begin
              J := PosText(#$D#$A, RemoteString, I);
              if J > 0 then
                Delete(RemoteString, I, J - I + 2);
            end;
            I := PosText(AnsiString('exception message'), RemoteString);
            if I > 0 then
            begin
              J := PosText(#$D#$A, RemoteString, I);
              if J > 0 then
                Delete(RemoteString, I, J - I + 2);
            end;
            Exit(DecodeUtf8(RemoteString));
          end
          else
          begin
            C := GetLastError;
            Exit(SysErrorMessage(C));
          end;
        finally
          CloseHandle(FileMappingHandle);
        end;
      end;
    finally
      CloseHandle(ProcessHandle);
    end;
  end;
  Result := Format('Process %d does not seem to embed madExcept.', [ProcessId]);
end;
madshi
Site Admin
Posts: 10753
Joined: Sun Mar 21, 2004 5:25 pm

Re: Getting a "frozen" bug report for a secondary exe file

Post by madshi »

Looks good, thanks.
Post Reply