Recent

Author Topic: Difference in ParamStr(0) between running with Debugger and running without  (Read 1155 times)

carl_caulkett

  • Hero Member
  • *****
  • Posts: 649
* Mac Mini M1
* macOS 14.6.1
* Lazarus 3.99
* FPC 3.3.1

While investigating an error posted by @Chiong in https://forum.lazarus.freepascal.org/index.php/topic,69173.msg536968.html#msg536968, I noticed that ParamStr(0) behaves differently when run with the debugger and without. Basically, without the debugger, ParamStr(0) reports the location of the actual Unix executable, whereas with debugging enabled (Dwarf 3 (beta)), the location of the symlink within the App bundle is reported.

To demonstrate, run the attached project (tweaked) with and without the debugger. The ShowMessage(ParamStr(0)); will display different messages. I've quickly knocked up a utility unit caPaths which ensure that @Chiong's program works in either debugging mode...
Code: Pascal  [Select][+][-]
  1. unit capaths;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils;
  9.  
  10. {$IFDEF DARWIN}
  11. const APP_BUNDLE_SUFFIX = '.app/Contents/MacOS';
  12. {$ENDIF}
  13.  
  14. type
  15.   Paths = class
  16.   private
  17.     class function Sep: string;
  18.   public
  19.     class function AppPath: string;
  20.     class function AppFolder: string;
  21.   end;
  22.  
  23. implementation
  24.  
  25. class function Paths.Sep: string;
  26. begin
  27.   Result := DirectorySeparator;
  28. end;
  29.  
  30. class function Paths.AppPath: string;
  31. var
  32.   Path: string;
  33.   AppPos: integer;
  34. begin
  35.   Path := ParamStr(0);
  36.   {$IFDEF DARWIN}
  37.   AppPos := Pos(APP_BUNDLE_SUFFIX, Path);
  38.   if AppPos > 0 then
  39.     Path := Copy(Path, 1, AppPos - 1);
  40.   {$ENDIF}
  41.   Result := Path;
  42. end;
  43.  
  44. class function Paths.AppFolder: string;
  45. begin
  46.   Result := ExtractFileDir(AppPath);
  47. end;
  48.  
  49. end.
  50.  

This may be a known problem, for all I know. But it does cause problems if one is trying to use the app folder to load accompanying data files, and suchlike :o
"It builds... ship it!"

rvk

  • Hero Member
  • *****
  • Posts: 6572
I've quickly knocked up a utility unit caPaths which ensure that @Chiong's program works in either debugging mode...
Does that still result in retrieving an actual existing unix executable and not just a path??

I don't have Mac so I can't test... but I thought the .app is not the executable but that's in one of the subfolders in Contents/MacOS.

Note that paramstr(0) should actually return the executable and not a path.

What does Application.Exename return?

https://forum.lazarus.freepascal.org/index.php/topic,52585.msg388004.html#msg388004
Quote
Application.Exename uses the official function for that. The fact that it returns a symbolic link's location if that's how the application is started, is therefore by design. It also enables the Lazarus trick of creating an application bundle that contains a symbolic link to an executable outside the bundle.
« Last Edit: November 07, 2024, 09:49:47 am by rvk »

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10542
  • Debugger - SynEdit - and more
    • wiki
On Mac => So presumingly you are using the lldb + fpdebug backend?

Then you can look at: menu > IDE internals > debug output
to see what command lldb is given. What is set as the exe.

Can you run lldb from command line and set a different exe to get the behaviour you want?

carl_caulkett

  • Hero Member
  • *****
  • Posts: 649
Does that still result in retrieving an actual existing unix executable and not just a path??

Seems to !

I don't have Mac so I can't test... but I thought the .app is not the executable but that's in one of the subfolders in Contents/MacOS.

Not quite! The .app is the folder itself, but macOS treats it as an executable. When Lazarus produces such a .app package, there will also be be a Unix executable alongside that .app. Within the .app folder is this structure...

UPDATE: Sorry @rvk, I misread your comment. You're saying exactly what I then went on to explain!

Code: Text  [Select][+][-]
  1. ~/Code/fpc/SchuellersBook  tree SchuellersRepertory.app
  2. SchuellersRepertory.app
  3. └── Contents
  4.     ├── Info.plist
  5.     ├── MacOS
  6.     │   └── SchuellersRepertory -> ../../../SchuellersRepertory
  7.     ├── PkgInfo
  8.     └── Resources
  9.  
Note the SymLink within the MacOS folder, linking to the aforementioned Unix executable parallel to the .app bundle.

Note that paramstr(0) should actually return the executable and not a path.

It does!

What does Application.Exename return?

Same problem...
Without debugging... /Users/carlcaulkett/Code/fpc/SchuellersBook/SchuellersRepertory - referring to the Unix executable alongside the .app bundle.
With debugging... /Users/carlcaulkett/Code/fpc/SchuellersBook/SchuellersRepertory.app/Contents/MacOS/SchuellersRepertory - Referring to the SymLink to the Unix executable.

https://forum.lazarus.freepascal.org/index.php/topic,52585.msg388004.html#msg388004
Quote
Application.Exename uses the official function for that. The fact that it returns a symbolic link's location if that's how the application is started, is therefore by design. It also enables the Lazarus trick of creating an application bundle that contains a symbolic link to an executable outside the bundle.

But surely Application.ExeName should return the same information regardless of whether or not debugging is in operation!
« Last Edit: November 07, 2024, 02:11:42 pm by carl_caulkett »
"It builds... ship it!"

carl_caulkett

  • Hero Member
  • *****
  • Posts: 649
On Mac => So presumingly you are using the lldb + fpdebug backend?

I am.

Then you can look at: menu > IDE internals > debug output
to see what command lldb is given. What is set as the exe.

Process 49760 launched: '/Users/carlcaulkett/Code/fpc/SchuellersBook/SchuellersRepertory.app/Contents/MacOS/SchuellersRepertory' (arm64)

Can you run lldb from command line and set a different exe to get the behaviour you want?

I could, but the fact remains that with debugging, the SymLink is being treated as the executable, whereas, without debugging, the actual Unix executable is being treated as the executable. Since these have different path locations, it means that any code that tries to use that path location for, say locating, data files, is either going to have to cope with the difference, or is going to fail :o
"It builds... ship it!"

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10542
  • Debugger - SynEdit - and more
    • wiki
"Process launched" is the result.

At some time prior the IDE tells lldb what file to launch. (I don't recall the lldb command, using that to rarely).
So the IDE may give it the folder, the link, or the final exe. Which one?

And if you run lldb outside the IDE, and give it any of the other, what happens. Can lldb launch that exe in a way that it gives the same result?

I recall that I tested variations to see what is needed so lldb would actually work at all. Maybe there is only one, maybe there are many options and I took the first that worked.
So, can lldb actually be called in a way that it would give the same result? Because if lldb can't, then there is nothing we can do. (nothing that I would know of).

Sorry, but I am not currently testing this myself. I don't have a Mac, I can access one remotely, but given that every mouse click and keystroke does something different from what Linux and/or Window would do, I always spent a lot of time undoing unwanted actions... Hence, I wont be checking this myself.



How did you obtain the "non debug" result?

- Click on desktop?
- open the_folder
- ./the_folder/Content/MacOs/foo
- ...
?

Do all of them return the same result?

What dose "run without debug" in the IDE return?

carl_caulkett

  • Hero Member
  • *****
  • Posts: 649
How did you obtain the "non debug" result?

For the non-debug result, I'm running...

Run -> Run without Debugging in the IDE
or
Double Clicking the .app bundle from Finder.
or
open -a SchuellersRepertory.app from the command-line.

All three of these of these give Application.ExeName/ParamStr(0) as /Users/carlcaulkett/Code/fpc/SchuellersBook/SchuellersRepertory

I'm using Run -> Run in the IDE to run with the debugger.

This gives /Users/carlcaulkett/Code/fpc/SchuellersBook/SchuellersRepertory.app/Contents/MacOS/SchuellersRepertory

Sorry, but I am not currently testing this myself. I don't have a Mac, I can access one remotely, but given that every mouse click and keystroke does something different from what Linux and/or Window would do, I always spent a lot of time undoing unwanted actions... Hence, I wont be checking this myself.

No apologies necessary. It's funny how your experience of Lazarus in macOS is exactly the same as my experience of Lazarus in Linux and Windows  ;)


« Last Edit: November 07, 2024, 02:13:20 pm by carl_caulkett »
"It builds... ship it!"

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10542
  • Debugger - SynEdit - and more
    • wiki
So then, at some point the IDE send
Code: Text  [Select][+][-]
  1. target create path_file
to lldb.

And the question is, can that  be changed, so that the param(0) changes?

And more, there were issues (not sure if related) that lldb failed, either m2, or intel, or cross between them. Not sure. But it is not known if changing this could create new issues... It would need wide spread testing.

carl_caulkett

  • Hero Member
  • *****
  • Posts: 649
So then, at some point the IDE send
Code: Text  [Select][+][-]
  1. target create path_file
to lldb.

And the question is, can that  be changed, so that the param(0) changes?

And more, there were issues (not sure if related) that lldb failed, either m2, or intel, or cross between them. Not sure. But it is not known if changing this could create new issues... It would need wide spread testing.

Maybe I'm being oversimplistic in asking this, but wouldn't it be easier to amend the code for ParamStr(0), which is where I presume the Application.ExeName comes from, to have something like the code I posted...
Code: Pascal  [Select][+][-]
  1. unit capaths;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils;
  9.  
  10. {$IFDEF DARWIN}
  11. const APP_BUNDLE_SUFFIX = '.app/Contents/MacOS';
  12. {$ENDIF}
  13.  
  14. type
  15.   Paths = class
  16.   private
  17.     class function Sep: string;
  18.   public
  19.     class function AppPath: string;
  20.     class function AppFolder: string;
  21.   end;
  22.  
  23. implementation
  24.  
  25. class function Paths.Sep: string;
  26. begin
  27.   Result := DirectorySeparator;
  28. end;
  29.  
  30. class function Paths.AppPath: string;
  31. var
  32.   Path: string;
  33.   AppPos: integer;
  34. begin
  35.   Path := ParamStr(0);
  36.   {$IFDEF DARWIN}
  37.   AppPos := Pos(APP_BUNDLE_SUFFIX, Path);
  38.   if AppPos > 0 then
  39.     Path := Copy(Path, 1, AppPos - 1);
  40.   {$ENDIF}
  41.   Result := Path;
  42. end;
  43.  
  44. class function Paths.AppFolder: string;
  45. begin
  46.   Result := ExtractFileDir(AppPath);
  47. end;
  48.  
  49. end.
  50.  

 
"It builds... ship it!"

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10542
  • Debugger - SynEdit - and more
    • wiki
How would one modify the ParamString? Other than changing the source code of the project that is being debugged, but that doesn't make sense?

I don't know who exactly it is gotten, but the OS is providing the data for ParamString (zero and others). And it does that based on how the app was called, afaik. And how the app was called is done by lldb.  The only influence (that I know of) that Lazarus has, is what it sets a "create target".

TRon

  • Hero Member
  • *****
  • Posts: 3618
Re: Difference in ParamStr(0) between running with Debugger and running without
« Reply #10 on: November 07, 2024, 04:36:33 pm »
Please forgive my ignorance on the subject but can't this be solved by checking if paramstr(0) is in fact a symlink and then resolve the link ? Is about 2 lines of code.
This tagline is powered by AI

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10542
  • Debugger - SynEdit - and more
    • wiki
Re: Difference in ParamStr(0) between running with Debugger and running without
« Reply #11 on: November 07, 2024, 04:42:59 pm »
Please forgive my ignorance on the subject but can't this be solved by checking if paramstr(0) is in fact a symlink and then resolve the link ? Is about 2 lines of code.

Well, any user can add that to their own application, so yes.

But, IMHO, the function ParamString should return whatever the OS says. Or follow OS convention.

I don't think correcting this in this function is the way to go. And if lldb can call the exe in a way that causes this then user might be able to (if starting from console, or whatever). And then there may even be projects that use ParamString to test for this. So changing ParamString could be seen as introducing a regression.

TRon

  • Hero Member
  • *****
  • Posts: 3618
Re: Difference in ParamStr(0) between running with Debugger and running without
« Reply #12 on: November 07, 2024, 04:56:00 pm »
@Martin_Fr:
Yes, I agree with that general note.

However, but I might be understanding the reported issue wrong, this seems to be particular about the difference in behaviour whether or not the application was started using the debugger.

So, when invoking the debugger the invocation function could check for this and revolve the 'proper' name and pass that the debugger ? I do not know what the 'normal' behaviour is for the debugger in use (e.g. whether or not it should actually be the debugger that does this to make sure the behaviour is the same)

I am not sure if doing such a thing could interfere with other expected behaviour.

FWIW: I am aware that (as advertised) code should not rely on paramStr(0) whatsoever for certain platforms.
« Last Edit: November 07, 2024, 05:03:24 pm by TRon »
This tagline is powered by AI

carl_caulkett

  • Hero Member
  • *****
  • Posts: 649
Re: Difference in ParamStr(0) between running with Debugger and running without
« Reply #13 on: November 07, 2024, 04:57:33 pm »
How would one modify the ParamString? Other than changing the source code of the project that is being debugged, but that doesn't make sense?

No! I meant altering the code within this function paramstr(l: longint) : shortstring; within ./fpcsrc/rtl/bsd/system.pp (this is the code revealed by the debugger, which makes sense because macOS is really FreeBSD in disguise ;))

URGENT UPDATE: The previous comment about macOS and FreeBSD was a joke, albeit one based on tenuous links with reality. Please do not spread rumours or any kind of misinformation based thereupon :o

Code: Pascal  [Select][+][-]
  1.  { variable where full path and filename and executable is stored }
  2.  { is setup by the startup of the system unit.                    }
  3. //var
  4. // execpathstr : shortstring;
  5.  
  6. function paramstr(l: longint) : shortstring;
  7.  begin
  8.    { stricly conforming POSIX applications  }
  9.    { have the executing filename as argv[0] }
  10. //   if l=0 then
  11. //     begin
  12. //       paramstr := execpathstr;
  13. //     end
  14. //   else
  15.      if (l >= 0) and (l < argc) then
  16.        paramstr:=strpas(argv[l])
  17.      else
  18.        paramstr:='';
  19.  end;
  20.  


There's already commented out code that deals with the special case of argv[0], though it seems that this was an attempt to tweak the behaviour which was abandoned...

« Last Edit: November 07, 2024, 10:55:25 pm by carl_caulkett »
"It builds... ship it!"

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10542
  • Debugger - SynEdit - and more
    • wiki
Re: Difference in ParamStr(0) between running with Debugger and running without
« Reply #14 on: November 07, 2024, 06:10:37 pm »
@TRon:

The debugger does not invoke that function. The debugger tells the OS to start a process, giving the os stuff like: the current dir, the full name of the process, the command line args, env-vars....

The app (running inside or outside the debugger) then queries that (from the OS) and does whatever it needs to do.

On Mac, you can start an exe by "opening" the app bundle (the normal way), or by invoking the exe in the app bundle.

The debugger currently does the latter. On Mac, in this context the debugger involves lldb, and to an extend what the IDE tells lldb to do. => And I don't currently know, if lldb can be told to do it in some other way.




@Carl_caulkett

You can approach the fpc team on that. I am doubtful of this having any chance... But I am not part of the FPC team, so ...

It also means that it would only work when using those methods. Any raw access would still disclose that the debugger caused a difference.

 

TinyPortal © 2005-2018