Recent

Author Topic: [Beginner] Crash in TProcess Pipe access  (Read 686 times)

colo

  • Newbie
  • Posts: 4
[Beginner] Crash in TProcess Pipe access
« on: May 01, 2022, 12:38:54 pm »
Dear Lazarus community,

I've started playing around with this nifty and very impressive language and IDE of yours, and tried to implement a little Windows GUI application to run external console programs with. I do not have any programming experience with either Windows nor Pasal/Delphi to speak of, and have some difficulty in assessing I am doing wrong about a runtime failure I experience, which is why I turn to you to ask for help.

I adapted some example code from the wiki and StackOverflow to have a child process running in the background with my application communication with it over its stdio descriptors. It seems like something during the execution goes wrong, and there might be some memory corruption involved. My x86 ASM is too limited to know what exactly is going on, but the memory address shown in the crash message does not look right to me.

My procedure:
Code: Pascal  [Select][+][-]
  1. function RunExternalAppInMemo(AppPath: string; ArgV: TStringArray;
  2.   OutMemo: TMemo; ErrMemo: TMemo): integer;
  3. const
  4.   READ_BYTES = 2048;
  5. var
  6.   aProcess: TProcess;
  7.   NumBytes: longint;
  8.   Buffer: array of byte;
  9.   ErrBuffer: array of byte;
  10.   ReadCount: integer;
  11.   LastRead: longint;
  12.   ExitCode: integer;
  13.   i: integer;
  14.  
  15. begin
  16.   Buffer := [];
  17.   ErrBuffer := [];
  18.   SetLength(Buffer, READ_BYTES + 1);
  19.   SetLength(ErrBuffer, READ_BYTES + 1);
  20.   LastRead := -1;
  21.   ExitCode := -1;
  22.   aProcess := TProcess.Create(nil);
  23.   aProcess.Executable := AppPath;
  24.   for i := 0 to Length(ArgV) - 1 do
  25.   begin
  26.     aProcess.Parameters.Add(ArgV[i]);
  27.     ErrMemo.Lines.Add(ArgV[i]);
  28.   end;
  29.   aProcess.ShowWindow := swoHIDE;
  30.   //aProcess.Options := aProcess.Options + [poUsePipes] + [poStdErrToOutput];
  31.   aProcess.Options := aProcess.Options + [poUsePipes];
  32.   try;
  33.     //ShowMessage(BoolToStr(aProcess.Running));
  34.     aProcess.Execute;
  35.     //ShowMessage(IntToStr(aProcess.Output.NumBytesAvailable));
  36.  
  37.  
  38.     while aProcess.Running or (aProcess.Output.NumBytesAvailable > 0) or
  39.       (aProcess.Stderr.NumBytesAvailable > 0) do
  40.     begin
  41.       while aProcess.Output.NumBytesAvailable > 0 do
  42.       begin
  43.         ReadCount := Min(READ_BYTES, aProcess.Output.NumBytesAvailable);
  44.         ShowMessage('Have bytes stdout: ' + IntToStr(aProcess.Output.NumBytesAvailable) + '  -  Will read stdout: ' + IntToStr(ReadCount));
  45.         ShowMessage('Have bytes stderr: ' + IntToStr(aProcess.Stderr.NumBytesAvailable));
  46.         LastRead := aProcess.Output.Read(Buffer, ReadCount);
  47.         if (LastRead = 0) then ShowMessage('Read 0 bytes, EOF?');
  48.         ShowMessage('Have read:' + IntToStr(LastRead));
  49.         // OutMemo.Lines.Append(PChar(Buffer)); // TODO: manage and eval buffer intellgently to get at logical lines or records of output to append
  50.         application.ProcessMessages;
  51.         ShowMessage('About to crash in NumBytesAvailable...');
  52.         ShowMessage('Have bytes stdout: ' + IntToStr(aProcess.Output.NumBytesAvailable));
  53.         ShowMessage('First iter end');
  54.       end;
  55.  
  56.       while aProcess.Stderr.NumBytesAvailable > 0 do
  57.       begin
  58.         ReadCount := Min((READ_BYTES - 1), aProcess.Stderr.NumBytesAvailable);
  59.         aProcess.Stderr.Read(ErrBuffer, ReadCount);
  60.         ErrMemo.Lines.Add(PChar(ErrBuffer));
  61.         application.ProcessMessages;
  62.       end;
  63.       sleep(10);
  64.     end;
  65.     ExitCode := aProcess.ExitStatus;
  66.   finally
  67.     setlength(Buffer, 0);
  68.     setlength(ErrBuffer, 0);
  69.     aProcess.Free;
  70.   end;
  71.   Result := ExitCode;
  72. end;
  73.  

The crash message reads like this:
Quote
Project ProcRun raised exception class 'External: ACCESS VIOLATION' with message: Access violation reading from address $FFFF...FFFF.
In file 'unit1.pas' at line 109: ShowMessage('Have bytes stdout' + IntToStr(aProcess.Output.NumBytesAvailable));

Interacting with the TProcess instance that seems to be executing my dependant process just fine after having read from aProcess.Output.Read seems to trigger the crash.

I have attached a contrived example of my code that should be able to demonstrate the failure on Windows hosts that can build it.

Can anyone please enlighten me what I am droing wrong? Any help - as well as tips on how to use the debugger to learn more about the underlyzing cause of this crash - would be greatly appreciated!

[Edited to fix code tags. Please read How to use the forum.]
« Last Edit: May 02, 2022, 12:21:08 am by trev »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 10074
  • FPC developer.
Re: [Beginner] Crash in TProcess Pipe access
« Reply #1 on: May 01, 2022, 01:23:50 pm »

I don't see anything directly wrong, but it seems your original code is not based on 3.2.2 or trunk. You might want to see how it is now:

I'm not used to showmessage debugging though ( I usually debug to console or logfile without halting the process).

colo

  • Newbie
  • Posts: 4
Re: [Beginner] Crash in TProcess Pipe access
« Reply #2 on: May 01, 2022, 01:41:58 pm »
Thanks a lot for chiming in! :)

I don't see anything directly wrong, but it seems your original code is not based on 3.2.2 or trunk. You might want to see how it is now

Thanks for the pointer - my Lazarus IDE tells me that I am using FPC 3.2.2, so from a library/source code perspective, I should be OK, right? As for API usage, I've tried to stick to documented un-deprecated usage (such as steering away from TProcess.CommandLine). Since I still have some issues with consuming both Free Pascal source code and its usual documentation style, I'm not sure what difference in usage I should I aim for... if you could elaborate on that, I would be very grateful!


I'm not used to showmessage debugging though ( I usually debug to console or logfile without halting the process).

Me neiher :D But I haven't programmed a non-web GUI application in more than two decades, and that was the very first (and acceptably quick to implement) method that I could come up with. I am more familiar with workflows involving print() "debugging" and using an actual debugger (gdb, but outside of a full-blown IDE) on the shell, but since I'm in doubly unfamiliar territory (Pascal and Windows), I'm not sure what I even could expect. Is there a recommended introduction into effective debugging with the Lazarus IDE that I should consume?

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 10074
  • FPC developer.
Re: [Beginner] Crash in TProcess Pipe access
« Reply #3 on: May 01, 2022, 01:44:46 pm »
Meanwhile, I tested your code (with FPC 3.3.1, the only one that I have a lazarus for) and it doesn't crash.

I don't see any output however (while bytes were read)

ADDED later: I see the output memo is disabled, fixing that makes it work.
« Last Edit: May 01, 2022, 01:47:02 pm by marcov »

colo

  • Newbie
  • Posts: 4
Re: [Beginner] Crash in TProcess Pipe access
« Reply #4 on: May 01, 2022, 02:42:29 pm »
Huh, that is... unexpected? I tried to reproduce the crash now when compiling the same code (sans the unneeded, yet included, Windows-specifics that I bluntly ripped out to make it compile), and I get a SIGSEGV in very much the same way as I do get an exception on Windows 10. If I use it to invoke exeutable that do not have any stdio output (/bin/true, /bin/false) then it manages to run to completion. Still I am stumped to understand what is going on - if anyone on FPC 3.2.2 could try to reproduce, I'd be grateful.

I've tried to look for resources on how to integrate different FPC releases into Lazarus, but it's still unclear to me how to do that. Can anyon point me to the docs on how to swith the compiler used within Lazarus?

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 10074
  • FPC developer.
Re: [Beginner] Crash in TProcess Pipe access
« Reply #5 on: May 01, 2022, 03:53:14 pm »
Oh, and pass buffer[0] and errbuffer[0] to read.

Passing the dynamic variable itself might lead to problems, it might read into the pointer, not the data)
« Last Edit: May 01, 2022, 03:58:23 pm by marcov »

colo

  • Newbie
  • Posts: 4
Re: [Beginner] Crash in TProcess Pipe access
« Reply #6 on: May 01, 2022, 05:23:33 pm »
Oh, and pass buffer[0] and errbuffer[0] to read.

Passing the dynamic variable itself might lead to problems, it might read into the pointer, not the data)

THAT was it, apparently! Thanks so much; I will have to carefully study what those identifiers I keep throwing around with very little actualunderstading translate to on the machine level rather sooner than later :)

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 10074
  • FPC developer.
Re: [Beginner] Crash in TProcess Pipe access
« Reply #7 on: May 01, 2022, 05:24:09 pm »
Glad to be of service  ;D

MarkMLl

  • Hero Member
  • *****
  • Posts: 4392
Re: [Beginner] Crash in TProcess Pipe access
« Reply #8 on: May 01, 2022, 07:03:00 pm »
THAT was it, apparently! Thanks so much; I will have to carefully study what those identifiers I keep throwing around with very little actualunderstading translate to on the machine level rather sooner than later :)

I'd missed that, I was looking at the lifetime of aProcess etc..

Something like a dynamic array or string only has a small amount of state stored locally (i.e. on the stack etc.), so e.g. @myBuffer or @myString is the address of an opaque data structure which contains a pointer to the data itself which is on the heap. So for a dynamic array the address of the first actual element is @myBuffer[0], while for a string it would be @myString[1] since element zero is reserved for historical reasons.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

 

TinyPortal © 2005-2018