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

delphi package - automated exception handling

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

Postby obones » Thu Mar 21, 2019 9:38 am

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.
obones
 
Posts: 60
Joined: Fri May 15, 2009 11:47 am

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

Postby madshi » Thu Mar 21, 2019 10:19 am

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.
madshi
Site Admin
 
Posts: 10301
Joined: Sun Mar 21, 2004 5:25 pm

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

Postby obones » Fri Mar 22, 2019 8:09 am

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.
obones
 
Posts: 60
Joined: Fri May 15, 2009 11:47 am

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

Postby madshi » Mon Mar 25, 2019 8:50 am

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.
madshi
Site Admin
 
Posts: 10301
Joined: Sun Mar 21, 2004 5:25 pm

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

Postby obones » Mon Mar 25, 2019 9:01 am

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;
obones
 
Posts: 60
Joined: Fri May 15, 2009 11:47 am

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

Postby madshi » Mon Mar 25, 2019 9:19 am

Looks good, thanks.
madshi
Site Admin
 
Posts: 10301
Joined: Sun Mar 21, 2004 5:25 pm


Return to madExcept

Who is online

Users browsing this forum: No registered users and 13 guests