Recent

Author Topic: Linux command "time" equivalent  (Read 237 times)

backprop

  • Sr. Member
  • ****
  • Posts: 255
Linux command "time" equivalent
« on: July 03, 2026, 11:48:14 am »
Does anyone tried to create equivalent of linux 'time' command in FPC?
Is it possible at least?

The command shows not simply elapsed time of execution, but three values: real, user and sys.
« Last Edit: July 03, 2026, 11:58:08 am by backprop »

Thaddy

  • Hero Member
  • *****
  • Posts: 19433
  • Glad to be alive.
Re: Linux command "time" equivalent
« Reply #1 on: July 03, 2026, 03:56:56 pm »
A - very rough and not complete - attempt:
Code: Pascal  [Select][+][-]
  1. program ftime;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   BaseUnix, Unix, SysUtils;
  7. const
  8. _SC_CLK_TCK = 2;
  9. function sysconf(name: cint): clong; cdecl; external 'c';
  10.  
  11. function SecondsBetween(const A, B: TTimeVal): Double;
  12. begin
  13.   Result :=
  14.     (B.tv_sec - A.tv_sec) +
  15.     (B.tv_usec - A.tv_usec) / 1000000.0;
  16. end;
  17.  
  18. function ClockTicksPerSecond: LongInt;
  19. begin
  20.   Result := SysConf(_SC_CLK_TCK);
  21.   if Result <= 0 then
  22.     Result := 100;
  23. end;
  24.  
  25. procedure PrintUsage;
  26. begin
  27.   Writeln('usage: ftime command [args...]');
  28. end;
  29.  
  30. var
  31.   StartTime, EndTime: TTimeVal;
  32.   StartTms, EndTms: Tms;
  33.   Ticks: LongInt;
  34.   Pid, Waited: TPid;
  35.   Status: cint;
  36.   Args: array of PChar;
  37.   I: Integer;
  38.   RealSeconds: Double;
  39.   UserSeconds: Double;
  40.   SysSeconds: Double;
  41.  
  42. begin
  43. {
  44.   if ParamCount < 1 then
  45.   begin
  46.     PrintUsage;
  47.     Halt(2);
  48.   end;
  49. }
  50.   Ticks := ClockTicksPerSecond;
  51.  
  52.   SetLength(Args, ParamCount + 1);
  53.   for I := 1 to ParamCount do
  54.     Args[I - 1] := PChar(ParamStr(I));
  55.   Args[ParamCount] := nil;
  56.  
  57.   fpGetTimeOfDay(@StartTime, nil);
  58.   fpTimes(StartTms);
  59.  
  60.   Pid := fpFork;
  61.  
  62.   if Pid = 0 then
  63.   begin
  64.     fpExecVP(Args[0], @Args[0]);
  65.  
  66.     Writeln(StdErr, 'ftime: cannot execute "', ParamStr(1), '": ', fpGetErrNo);
  67.     Halt(127);
  68.   end
  69.   else if Pid < 0 then
  70.   begin
  71.     Writeln(StdErr, 'ftime: fork failed: ', fpGetErrNo);
  72.     Halt(126);
  73.   end;
  74.  
  75.   repeat
  76.     Waited := fpWaitPid(Pid, Status, 0);
  77.   until (Waited = Pid) or ((Waited = -1) and (fpGetErrNo <> ESysEINTR));
  78.  
  79.   fpTimes(EndTms);
  80.   fpGetTimeOfDay(@EndTime, nil);
  81.  
  82.   RealSeconds := SecondsBetween(StartTime, EndTime);
  83.  
  84.   UserSeconds :=
  85.     (EndTms.tms_cutime - StartTms.tms_cutime) / Ticks;
  86.  
  87.   SysSeconds :=
  88.     (EndTms.tms_cstime - StartTms.tms_cstime) / Ticks;
  89.  
  90.   Writeln;
  91.   Writeln('real ', RealSeconds:0:3, 's');
  92.   Writeln('user ', UserSeconds:0:3, 's');
  93.   Writeln('sys  ', SysSeconds:0:3, 's');
  94.  
  95.   if Waited = -1 then
  96.     Halt(125);
  97. {
  98. // not supported (yet)
  99.   if fpWIFEXITED(Status) then
  100.     Halt(fpWEXITSTATUS(Status));
  101.  
  102.   if fpWIFSIGNALED(Status) then
  103.     Halt(128 + fpWTERMSIG(Status));
  104.  
  105.   Halt(1);}
  106. end.
But it works w/o parameters on Linux.

On my machine it outputs:
Code: Bash  [Select][+][-]
  1. ftime: cannot execute "": 2
  2.  
  3. real 0.081s
  4. user 0.000s
  5. sys  0.010s
That is because I still have to implement the parameters.
« Last Edit: July 03, 2026, 03:58:43 pm by Thaddy »
Any "programmer" that knows only one programming language is not a programmer

Thaddy

  • Hero Member
  • *****
  • Posts: 19433
  • Glad to be alive.
Re: Linux command "time" equivalent
« Reply #2 on: July 03, 2026, 04:03:47 pm »
Try this fixed version: I got some help from codex here to debug it:
Code: Pascal  [Select][+][-]
  1. program ftime;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   BaseUnix, Unix, SysUtils;
  7.  
  8. //-------------not implemented in FreePascal BaseUnix-----
  9. const
  10.   _SC_CLK_TCK = 2;
  11. function sysconf(name: cint): clong; cdecl; external 'c';
  12. //--------------------------------------------------------
  13.  
  14. function WIFEXITED(Status: cint): Boolean;
  15. begin
  16.   Result := (Status and $7F) = 0;
  17. end;
  18.  
  19. function WEXITSTATUS(Status: cint): cint;
  20. begin
  21.   Result := (Status shr 8) and $FF;
  22. end;
  23.  
  24. function WIFSIGNALED(Status: cint): Boolean;
  25. begin
  26.   Result := ((Status and $7F) <> 0) and ((Status and $7F) <> $7F);
  27. end;
  28.  
  29. function WTERMSIG(Status: cint): cint;
  30. begin
  31.   Result := Status and $7F;
  32. end;
  33.  
  34. function SecondsBetween(const A, B: TTimeVal): Double;
  35. begin
  36.   Result :=
  37.     (B.tv_sec - A.tv_sec) +
  38.     (B.tv_usec - A.tv_usec) / 1000000.0;
  39. end;
  40.  
  41. function ClockTicksPerSecond: LongInt;
  42. begin
  43.   Result := sysconf(_SC_CLK_TCK);
  44.   if Result <= 0 then
  45.     Result := 100;
  46. end;
  47.  
  48. procedure PrintTimes(
  49.   const StartTime, EndTime: TTimeVal;
  50.   const StartTms, EndTms: Tms;
  51.   Ticks: LongInt);
  52. var
  53.   RealSeconds, UserSeconds, SysSeconds: Double;
  54. begin
  55.   RealSeconds := SecondsBetween(StartTime, EndTime);
  56.  
  57.   UserSeconds :=
  58.     (EndTms.tms_cutime - StartTms.tms_cutime) / Ticks;
  59.  
  60.   SysSeconds :=
  61.     (EndTms.tms_cstime - StartTms.tms_cstime) / Ticks;
  62.  
  63.   Writeln;
  64.   Writeln('real ', RealSeconds:0:3, 's');
  65.   Writeln('user ', UserSeconds:0:3, 's');
  66.   Writeln('sys  ', SysSeconds:0:3, 's');
  67. end;
  68.  
  69. var
  70.   StartTime, EndTime: TTimeVal;
  71.   StartTms, EndTms: Tms;
  72.   Ticks: LongInt;
  73.   Pid, Waited: TPid;
  74.   Status: cint;
  75.   Err: cint;
  76.   Args: array of PChar;
  77.   I: Integer;
  78.  
  79. begin
  80.   Ticks := ClockTicksPerSecond;
  81.  
  82.   fpGetTimeOfDay(@StartTime, nil);
  83.   fpTimes(StartTms);
  84.  
  85.   if ParamCount > 0 then
  86.   begin
  87.     SetLength(Args, ParamCount + 1);
  88.  
  89.     for I := 1 to ParamCount do
  90.       Args[I - 1] := PChar(ParamStr(I));
  91.  
  92.     Args[ParamCount] := nil;
  93.  
  94.     Pid := fpFork;
  95.  
  96.     if Pid = 0 then
  97.     begin
  98.       fpExecVP(Args[0], @Args[0]);
  99.       Writeln(StdErr, 'ftime: cannot execute "', ParamStr(1), '": ', fpGetErrNo);
  100.       fpExit(127);
  101.     end
  102.     else if Pid < 0 then
  103.     begin
  104.       Writeln(StdErr, 'ftime: fork failed: ', fpGetErrNo);
  105.       Halt(126);
  106.     end;
  107.  
  108.     repeat
  109.       Waited := fpWaitPid(Pid, Status, 0);
  110.       Err := fpGetErrNo;
  111.     until (Waited = Pid) or ((Waited = -1) and (Err <> ESysEINTR));
  112.   end
  113.   else
  114.   begin
  115.     Waited := 0;
  116.     Status := 0;
  117.   end;
  118.  
  119.   fpTimes(EndTms);
  120.   fpGetTimeOfDay(@EndTime, nil);
  121.  
  122.   PrintTimes(StartTime, EndTime, StartTms, EndTms, Ticks);
  123.  
  124.   if Waited = -1 then
  125.     Halt(125);
  126.  
  127.   if ParamCount = 0 then
  128.     Halt(0);
  129.  
  130.   if WIFEXITED(Status) then
  131.     Halt(WEXITSTATUS(Status));
  132.  
  133.   if WIFSIGNALED(Status) then
  134.     Halt(128 + WTERMSIG(Status));
  135.  
  136.   Halt(1);
  137. end.
I was close in my previous attempt, though... :D :)

A bit frustrating that FPC does not implement sysconf itself as fpsysconf.
I will write a patch for at least Linux and Mac. (the constants differ quite a bit, maybe it is not implemented because of that? Marcov? It should be standard POSIX 1003.2)

I used the C code and translated it, so unless I (or my debugging friend codex) made mistakes this should be an 1 to 1 translation for the core features..

Feel free to clean up the code and improve it, but this simply works on Linux and I am now testing on my Mac Mini.
Maybe inlining can be even more accurate.

This is NOT a complete implementation!!!! I do not pretend it is complete, that takes more than a Friday afternoon.

[edit] the code also works on the Mac, Phew....
« Last Edit: July 03, 2026, 04:46:13 pm by Thaddy »
Any "programmer" that knows only one programming language is not a programmer

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12948
  • FPC developer.
Re: Linux command "time" equivalent
« Reply #3 on: July 03, 2026, 06:01:45 pm »
A bit frustrating that FPC does not implement sysconf itself as fpsysconf.
I will write a patch for at least Linux and Mac. (the constants differ quite a bit, maybe it is not implemented because of that? Marcov? It should be standard POSIX 1003.2)

libc only functions are not part of the RTL, as the core focus was still on syscalls. If somebody is interested it, its logical place would be a package that could be made to depend on libc, like the users package.


Thaddy

  • Hero Member
  • *****
  • Posts: 19433
  • Glad to be alive.
Re: Linux command "time" equivalent
« Reply #4 on: July 03, 2026, 06:20:28 pm »
Thanks Marco,

I will attempt sysconf only for now, since that is the most urgent that is lacking.
Any "programmer" that knows only one programming language is not a programmer

 

TinyPortal © 2005-2018