Recent

Author Topic: impossible bug under ARM  (Read 5444 times)

TCH

  • Full Member
  • ***
  • Posts: 200
impossible bug under ARM
« on: August 28, 2018, 11:43:02 pm »
I'm currently facing something unreal.

The problem is, that some part of the code does not triggered, when it needed, but when i put logging lines there (no other changes!), then it works, so i cannot debug it, what it is the problem.

The only difference was, once i managed to debug out that one of the variables was 0, instead of 500, but that variable was set constantly in the beginning and nowhere else it was touched in the code.

What can cause this? I turned off optimization and smartlinking. The program has two other threads, but until this point in development nothing was wrong. (At least no such bugs.)

It's FreePascal 3.0.2 under Armbian and Orange Pi.

Thaddy

  • Hero Member
  • *****
  • Posts: 14377
  • Sensorship about opinions does not belong here.
Re: impossible bug under ARM
« Reply #1 on: August 29, 2018, 06:39:09 am »
without code this is hard to tell.
Does the compiler give any feedback like "variable x is declared but never used."?
Try to compile with -Sew, solve everything where it stops and give us all that the compiler spits out, should be just hints.
(Don't be pedantic, solve everything. Compiler knows best. Only idiots ignore warnings)
Do you check function results? Does the variable rely on OS functions that you call as procedure? (I saw once you tend to NOT check OS calls, like fpOpen etc, hence this remark)
Is the variable accessed from one of the threads?
Because I had something similar (my mistake!) that could be solved by simply accessing the variable once explicitly from the thread in which it was declared.
Put {$optimizations off} on top of the unit where the variable is declared, just to be sure.

But without code that reproduces the problem this remains guessing.
Start by at least showing us the compiler output and start with fixing all warnings and check all OS calls for validity.

« Last Edit: August 29, 2018, 06:46:38 am by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

TCH

  • Full Member
  • ***
  • Posts: 200
Re: impossible bug under ARM
« Reply #2 on: August 29, 2018, 10:12:54 am »
No, the compiler gives no warnings. The linker gives the usual warning: "link.res contains output sections; did you forget -T?"

I compiled it with the sole parameter -Sew, here is the output:
Code: Text  [Select][+][-]
  1. Free Pascal Compiler version 3.0.2 [2017/02/17] for arm
  2. Copyright (c) 1993-2017 by Florian Klaempfl and others
  3. Target OS: Linux for ARMHF
  4. Compiling xc0x.pas
  5. Compiling ./pascal_units/tcpipserver.pas
  6. Compiling config.pas
  7. Compiling ./pascal_units/elif_parser.pas
  8. Compiling ./pascal_units/strarrfunc.pas
  9. Assembling strarrfunc
  10. Assembling elif_parser
  11. Compiling ./pascal_units/log.pas
  12. Assembling log
  13. Compiling ./pascal_units/rcmkdir.pas
  14. Assembling rcmkdir
  15. Assembling config
  16. Assembling tcpipserver
  17. Compiling ./pascal_units/sockerrmsg.pas
  18. Assembling sockerrmsg
  19. Compiling ./pascal_units/usbkeyb.pas
  20. Assembling usbkeyb
  21. Compiling ./pascal_units/serialport.pas
  22. Assembling serialport
  23. Compiling ./pascal_units/signalhandler.pas
  24. Assembling signalhandler
  25. Compiling ./pascal_units/daemon.pas
  26. Assembling daemon
  27. Compiling ./pascal_units/tstamp64.pas
  28. Assembling tstamp64
  29. Compiling ./pascal_units/sfutils.pas
  30. Assembling sfutils
  31. Compiling ./pascal_units/pinmatrix.pas
  32. Compiling ./pascal_units/el_gpio.pas
  33. Assembling el_gpio
  34. Assembling pinmatrix
  35. Compiling ac01_protocol.pas
  36. Compiling ./pascal_units/shexec.pas
  37. Assembling shexec
  38. Compiling web.pas
  39. Assembling web
  40. Assembling ac01_protocol
  41. Assembling xc0x
  42. Linking xc0x
  43. /usr/bin/ld: warning: link.res contains output sections; did you forget -T?

I do check function results, unless their result is irrelevant. What you saw in my GPIO unit was the result of a quickly thrown together unit where i forgot to check the result of memory mappings. I already fixed that.

In the meantime i found where the variable is overwritten, in the TCP/IP server unit at the fpSelect. The weird thing is that variable is in the main thread and aside the initializing and config reloading, nothing ever touch it. I cannot share all code, because it is a company project, but the TCP/IP server unit is a generic stuff, not a secret one, so here it's code (the relevant line marked, but still i fear, that the bug is not there), but this was my first network code ever, so don't be surprised, if something is solved not properly inside:
Code: Pascal  [Select][+][-]
  1. unit TCPIPServer;
  2.  
  3. {$optimization off}
  4.  
  5. {$mode objfpc}{$H+}
  6.  
  7. interface
  8.  
  9. uses classes, sockets, baseunix;
  10.  
  11. const
  12.         FUNC_CODE_SOCKET   = 1;
  13.         FUNC_CODE_BIND     = 2;
  14.         FUNC_CODE_LISTEN   = 3;
  15.         FUNC_CODE_ACCEPT   = 4;
  16.         FUNC_CODE_RECV     = 5;
  17.         FUNC_CODE_SEND     = 6;
  18.  
  19. type
  20.         callback_procedure = procedure(c: pointer);
  21.  
  22.         TTCPIPServerCommArgs = record
  23.                 socket_to_listen: longint;
  24.                 client_index: longint;
  25.                 bufsize: longword;
  26.                 client_received_callback: callback_procedure;
  27.                 client_error_callback: callback_procedure;
  28.                 client_closed_callback: callback_procedure;
  29.                 client_sent_callback: callback_procedure;
  30.         end;
  31.         PTCPIPServerCommArgs = ^TTCPIPServerCommArgs;
  32.  
  33.         TTCPIPServerArgs = record
  34.                 port: word;
  35.                 backlog: byte;
  36.                 bufsize: longword;
  37.                 client_created_callback: callback_procedure;
  38.                 client_received_callback: callback_procedure;
  39.                 client_error_callback: callback_procedure;
  40.                 client_closed_callback: callback_procedure;
  41.                 client_sent_callback: callback_procedure;
  42.         end;
  43.         PTCPIPServerArgs = ^TTCPIPServerArgs;
  44.  
  45.         TTCPIPServerCommThread = class(TThread)
  46.                 private
  47.                 protected
  48.                         ts0, ts1: timespec;
  49.                         ListenSocket: longint;
  50.                         ClientAddr: TInetSockAddr;
  51.                         ClientAddrSize: LongInt;
  52.                         ClientSocket: Longint;
  53.                         c_client_index: longint;
  54.                         outBuf: pbyte;
  55.                         outBufSize: longword;
  56.                         Buffer: pbyte;
  57.                         c_BufferContentSize: longword;
  58.                         CLIENT_BUFFER_SIZE: longword;
  59.                         ERROR_FUNC_CODE, SOCKET_ERROR_CODE: longint;
  60.                         FDS: TFDSet;
  61.                         c_client_received_callback: callback_procedure;
  62.                         c_client_error_callback: callback_procedure;
  63.                         c_client_closed_callback: callback_procedure;
  64.                         c_client_sent_callback: callback_procedure;
  65.                         procedure Execute; override;
  66.                 public
  67.                         Constructor Create(args: PTCPIPServerCommArgs);
  68.                         function ClientIndex: LongInt;
  69.                         procedure GetLastError(func_error: plongint; socket_error: plongint);
  70.                         procedure FreeBuffer;
  71.                         procedure RecvAck;
  72.                         function BufferContentSize: longword;
  73.                         function BufferPointer: pbyte;
  74.                         procedure Send(buf: pbyte; size: longword);
  75.                         function OutBufferEmpty: boolean;
  76.                         procedure FinishClient(HaltTime: longword);
  77.         end;
  78.         PTCPIPServerCommThread = ^TTCPIPServerCommThread;
  79.  
  80.         TTCPIPServerThread = class(TThread)
  81.                 private
  82.                 protected
  83.                         ts0, ts1: timespec;
  84.                         srv_port: word;
  85.                         srv_backlog: byte;
  86.                         ListenSocket: longint;
  87.                         ServerAddr: TInetSockAddr;
  88.                         clients: array of TTCPIPServerCommThread;
  89.                         client_count: longint;
  90.                         thrargs: TTCPIPServerCommArgs;
  91.                         FDS: TFDSet;
  92.                         ERROR_FUNC_CODE, SOCKET_ERROR_CODE: longint;
  93.                         CLIENT_BUFFER_SIZE: longword;
  94.                         last_client: longint;
  95.                         srv_client_created_callback: callback_procedure;
  96.                         srv_client_received_callback: callback_procedure;
  97.                         srv_client_error_callback: callback_procedure;
  98.                         srv_client_closed_callback: callback_procedure;
  99.                         srv_client_sent_callback: callback_procedure;
  100.                         procedure Execute; override;
  101.                 public
  102.                         Constructor Create(args: PTCPIPServerArgs);
  103.                         procedure GetLastError(func_error: plongint; socket_error: plongint);
  104.                         procedure GetClientLastError(index: longint; func_error: plongint; socket_error: plongint);
  105.                         function ClientCount: longint;
  106.                         function LastCreatedClient: longint;
  107.                         procedure RecvAck(index: longint);
  108.                         function BufferContentSize(index: longint): longword;
  109.                         function BufferPointer(index: longint): pbyte;
  110.                         procedure Send(index: longint; buf: pbyte; size: longword);
  111.                         function OutBufferEmpty(index: longint): boolean;
  112.                         procedure FinishServer(HaltTime: longword);
  113.         end;
  114.  
  115. implementation
  116.  
  117. constructor TTCPIPServerCommThread.Create(args: PTCPIPServerCommArgs);
  118. begin
  119.         inherited Create(false);
  120.         FreeOnTerminate := False;
  121.         Self.Priority := tpLowest;
  122.         Self.ts0.tv_sec  := 0;
  123.         Self.ts0.tv_nsec := 50000000;
  124.         Self.ListenSocket := args^.socket_to_listen;
  125.         Self.c_client_index := args^.client_index;
  126.         Self.CLIENT_BUFFER_SIZE := args^.bufsize;
  127.         Self.c_BufferContentSize := 0;
  128.         Self.c_client_received_callback := args^.client_received_callback;
  129.         Self.c_client_error_callback := args^.client_error_callback;
  130.         Self.c_client_closed_callback := args^.client_closed_callback;
  131.         Self.c_client_sent_callback := args^.client_sent_callback;
  132.         Self.ClientAddrSize := sizeof(Self.ClientAddr);
  133.         Self.FDS := Default(TFDSet);
  134.         Self.ERROR_FUNC_CODE := 0;
  135.         Self.SOCKET_ERROR_CODE := 0;
  136.         Self.ClientSocket := fpaccept(Self.ListenSocket, @(Self.ClientAddr), @(Self.ClientAddrSize));
  137.         if (ClientSocket < 0) then
  138.         begin
  139.                 Self.ERROR_FUNC_CODE := FUNC_CODE_ACCEPT;
  140.                 Self.SOCKET_ERROR_CODE := SocketError;
  141.                 Self.Terminate;
  142.                 exit;
  143.         end;
  144.         Self.Buffer := GetMem(Self.CLIENT_BUFFER_SIZE);
  145. end;
  146.  
  147. procedure TTCPIPServerCommThread.Execute;
  148. var RD_Count, WT_Count: longint;
  149. begin
  150.         while (not Self.Terminated) do
  151.         begin
  152.                 if (Self.c_BufferContentSize = 0) then
  153.                 begin
  154.                         fpFD_Zero(Self.FDS);
  155.                         fpFD_Set(Self.ClientSocket, Self.FDS);
  156.                         fpSelect(Self.ClientSocket + 1, @(Self.FDS), nil, nil, 5000); // HERE BE BUGS...
  157.                         if ((not Self.Terminated) and (fpFD_ISSET(Self.ClientSocket, Self.FDS) <> 0)) then
  158.                         begin
  159.                                 RD_Count := fprecv(Self.ClientSocket, @(Self.Buffer[0]), Self.CLIENT_BUFFER_SIZE, 0);
  160.                                 if (RD_Count < 0) then
  161.                                 begin
  162.                                         Self.ERROR_FUNC_CODE := FUNC_CODE_RECV;
  163.                                         Self.SOCKET_ERROR_CODE := SocketError;
  164.                                         if (Self.c_client_error_callback <> nil) then
  165.                                         begin
  166.                                                 Self.c_client_error_callback(@(Self));
  167.                                         end;
  168.                                 end
  169.                                 else
  170.                                 begin
  171.                                         if (RD_Count > 0) then
  172.                                         begin
  173.                                                 Self.c_BufferContentSize := RD_Count;
  174.                                                 if (Self.c_client_received_callback <> nil) then
  175.                                                 begin
  176.                                                         Self.c_client_received_callback(@(Self));
  177.                                                 end;
  178.                                         end
  179.                                         else
  180.                                         begin
  181.                                                 Self.Terminate;
  182.                                         end;
  183.                                 end;
  184.                         end;
  185.                 end;
  186.                 if (Self.OutBufSize > 0) then
  187.                 begin
  188.                         WT_Count := fpSend(Self.ClientSocket, Self.OutBuf, Self.OutBufSize, 0);
  189.                         if (WT_Count < 0) then
  190.                         begin
  191.                                 Self.ERROR_FUNC_CODE := FUNC_CODE_SEND;
  192.                                 Self.SOCKET_ERROR_CODE := SocketError;
  193.                                 if (Self.c_client_error_callback <> nil) then
  194.                                 begin
  195.                                         Self.c_client_error_callback(@(Self));
  196.                                 end;
  197.                         end
  198.                         else
  199.                         begin
  200.                                 Self.OutBufSize := Self.OutBufSize - WT_Count;
  201.                                 Self.OutBuf := Self.OutBuf + WT_Count;
  202.                                 if (Self.OutBufSize = 0) then
  203.                                 begin
  204.                                         if (Self.c_client_sent_callback <> nil) then
  205.                                         begin
  206.                                                 Self.c_client_sent_callback(@(Self));
  207.                                         end;
  208.                                 end;
  209.                         end;
  210.                 end;
  211.                 if ((Self.c_BufferContentSize > 0) and (Self.OutBufSize = 0)) then
  212.                 begin
  213.                         fpnanosleep(@(Self.ts0), @(Self.ts1));
  214.                 end;
  215.         end;
  216.         CloseSocket(Self.ClientSocket);
  217.         if (Self.c_client_closed_callback <> nil) then
  218.         begin
  219.                 Self.c_client_closed_callback(@(Self));
  220.         end;
  221. end;
  222.  
  223. function TTCPIPServerCommThread.ClientIndex: LongInt;
  224. begin
  225.         Result := Self.c_client_index;
  226. end;
  227.  
  228. procedure TTCPIPServerCommThread.GetLastError(func_error: plongint; socket_error: plongint);
  229. begin
  230.         func_error^ := Self.ERROR_FUNC_CODE;
  231.         socket_error^ := Self.SOCKET_ERROR_CODE;
  232. end;
  233.  
  234. procedure TTCPIPServerCommThread.FreeBuffer;
  235. begin
  236.         FreeMem(Self.Buffer);
  237. end;
  238.  
  239. procedure TTCPIPServerCommThread.RecvAck;
  240. begin
  241.         Self.c_BufferContentSize := 0;
  242. end;
  243.  
  244. function TTCPIPServerCommThread.BufferContentSize: longword;
  245. begin
  246.         Result := Self.c_BufferContentSize;
  247. end;
  248.  
  249. function TTCPIPServerCommThread.BufferPointer: pbyte;
  250. begin
  251.         Result := Self.Buffer;
  252. end;
  253.  
  254. procedure TTCPIPServerCommThread.Send(buf: pbyte; size: longword);
  255. begin
  256.         Self.OutBuf := buf;
  257.         Self.OutBufSize := size;
  258. end;
  259.  
  260. function TTCPIPServerCommThread.OutBufferEmpty: boolean;
  261. begin
  262.         Result := Self.OutBufSize = 0;
  263. end;
  264.  
  265. procedure TTCPIPServerCommThread.FinishClient(HaltTime: longword);
  266. var count: longword;
  267. begin
  268.         if (Self.Finished or Self.Terminated) then
  269.         begin
  270.                 exit;
  271.         end;
  272.  
  273.         count := (HaltTime div 50) + 1;
  274.         Self.Terminate;
  275.         while ((not Self.Finished) and (count > 0)) do
  276.         begin
  277.                 fpnanosleep(@(Self.ts0), @(Self.ts1));
  278.                 dec(count);
  279.         end;
  280.         Self.FreeBuffer;
  281. end;
  282.  
  283. constructor TTCPIPServerThread.Create(args: PTCPIPServerArgs);
  284. begin
  285.         inherited Create(false);
  286.         FreeOnTerminate := False;
  287.         Self.Priority := tpLowest;
  288.         Self.ts0.tv_sec  := 0;
  289.         Self.ts0.tv_nsec := 50000000;
  290.         Self.client_count := 0;
  291.         setlength(Self.clients, Self.client_count);
  292.         Self.srv_port := args^.port;
  293.         Self.srv_backlog := args^.backlog;
  294.         Self.CLIENT_BUFFER_SIZE := args^.bufsize;
  295.         Self.srv_client_created_callback := args^.client_created_callback;
  296.         Self.srv_client_received_callback := args^.client_received_callback;
  297.         Self.srv_client_error_callback := args^.client_error_callback;
  298.         Self.srv_client_closed_callback := args^.client_closed_callback;
  299.         Self.srv_client_sent_callback := args^.client_sent_callback;
  300.         Self.ServerAddr.sin_family := AF_INET;
  301.         Self.ServerAddr.sin_addr.s_addr := INADDR_ANY;
  302.         Self.ServerAddr.sin_port := htons(Self.srv_port);
  303.         Self.FDS := Default(TFDSet);
  304.         Self.ERROR_FUNC_CODE := 0;
  305.         Self.SOCKET_ERROR_CODE := 0;
  306.         Self.ListenSocket := fpSocket (AF_INET, SOCK_STREAM, 0);
  307.         if (Self.ListenSocket < 0) then
  308.         begin
  309.                 Self.ERROR_FUNC_CODE := FUNC_CODE_SOCKET;
  310.                 Self.SOCKET_ERROR_CODE := SocketError;
  311.                 Self.Terminate;
  312.                 exit;
  313.         end;
  314.         fpSetSockOpt(Self.ListenSocket, SOL_SOCKET, SO_REUSEADDR, PChar('True'), Length('True'));
  315.         if (fpBind(Self.ListenSocket, @(Self.ServerAddr), sizeof(Self.ServerAddr)) < 0) then
  316.         begin
  317.                 Self.ERROR_FUNC_CODE := FUNC_CODE_BIND;
  318.                 Self.SOCKET_ERROR_CODE := SocketError;
  319.                 Self.Terminate;
  320.                 exit;
  321.         end;
  322.         if (fpListen(Self.ListenSocket, Self.srv_backlog + 1) < 0) then
  323.         begin
  324.                 Self.ERROR_FUNC_CODE := FUNC_CODE_LISTEN;
  325.                 Self.SOCKET_ERROR_CODE := SocketError;
  326.                 Self.Terminate;
  327.                 exit;
  328.         end;
  329.         thrargs.socket_to_listen := Self.ListenSocket;
  330.         thrargs.bufsize := Self.CLIENT_BUFFER_SIZE;
  331.         thrargs.client_received_callback := Self.srv_client_received_callback;
  332.         thrargs.client_error_callback := Self.srv_client_error_callback;
  333.         thrargs.client_closed_callback := Self.srv_client_closed_callback;
  334.         thrargs.client_sent_callback := Self.srv_client_sent_callback;
  335. end;
  336.  
  337. procedure TTCPIPServerThread.Execute;
  338. const ref: array[0..3] of char = ('N', '/', 'A', #0);
  339. var
  340.         i: integer;
  341.         f: boolean;
  342.         fe, se: longint;
  343.         ClientAddr: TInetSockAddr;
  344.         ClientAddrSize: LongInt;
  345.         ClientSocket: Longint;
  346. begin
  347.         ClientAddrSize := SizeOf(ClientAddr);
  348.         while (not Self.Terminated) do
  349.         begin
  350.                 fpFD_Zero(Self.FDS);
  351.                 fpFD_Set(Self.ListenSocket, Self.FDS);
  352.                 fpSelect(Self.ListenSocket + 1, @(Self.FDS), nil, nil, 5000);
  353.                 if ((not Self.Terminated) and (fpFD_ISSET(Self.ListenSocket, Self.FDS) <> 0)) then
  354.                 begin
  355.                         f := false;
  356.                         for i := 0 to Self.client_count - 1 do
  357.                         begin
  358.                                 if (Self.clients[i].Finished) then
  359.                                 begin
  360.                                         f := true;
  361.                                         break;
  362.                                 end;
  363.                         end;
  364.                         if (f) then
  365.                         begin
  366.                                 Self.clients[i].Free;
  367.                         end
  368.                         else
  369.                         begin
  370.                                 f := Self.client_count < Self.srv_backlog;
  371.                                 if (f) then
  372.                                 begin
  373.                                         i := Self.client_count;
  374.                                         inc(Self.client_count);
  375.                                         setlength(Self.clients, Self.client_count);
  376.                                 end;
  377.                         end;
  378.                         if (f) then
  379.                         begin
  380.                                 Self.last_client := i;
  381.                                 thrargs.client_index := Self.last_client;
  382.                                 Self.clients[Self.last_client] := TTCPIPServerCommThread.Create(@thrargs);
  383.                                 Self.GetClientLastError(Self.last_client, @fe, @se);
  384.                                 if ((Self.srv_client_created_callback <> nil) and (fe = 0)) then
  385.                                 begin
  386.                                         Self.srv_client_created_callback(@(Self.clients[Self.last_client]));
  387.                                 end;
  388.                         end
  389.                         else
  390.                         begin
  391.                                 ClientSocket := fpaccept(Self.ListenSocket, @ClientAddr, @ClientAddrSize);
  392.                                 if (ClientSocket >= 0) then
  393.                                 begin
  394.                                         fpSend(ClientSocket, @ref[0], 4, 0);
  395.                                         CloseSocket(ClientSocket);
  396.                                 end;
  397.                         end;
  398.                 end;
  399.         end;
  400.         CloseSocket(Self.ListenSocket);
  401. end;
  402.  
  403. procedure TTCPIPServerThread.GetLastError(func_error: plongint; socket_error: plongint);
  404. begin
  405.         func_error^ := Self.ERROR_FUNC_CODE;
  406.         socket_error^ := Self.SOCKET_ERROR_CODE;
  407. end;
  408.  
  409. procedure TTCPIPServerThread.GetClientLastError(index: longint; func_error: plongint; socket_error: plongint);
  410. begin
  411.         if (index < Self.client_count) then
  412.         begin
  413.                 Self.clients[index].GetLastError(func_error, socket_error);
  414.         end;
  415. end;
  416.  
  417. function TTCPIPServerThread.ClientCount: longint;
  418. begin
  419.         Result := Self.client_count;
  420. end;
  421.  
  422. function TTCPIPServerThread.LastCreatedClient: longint;
  423. begin
  424.         Result := Self.last_client;
  425. end;
  426.  
  427. procedure TTCPIPServerThread.RecvAck(index: longint);
  428. begin
  429.         if (index < Self.client_count) then
  430.         begin
  431.                 Self.clients[index].RecvAck;
  432.         end;
  433. end;
  434.  
  435. function TTCPIPServerThread.BufferContentSize(index: longint): longword;
  436. begin
  437.         if (index < Self.client_count) then
  438.         begin
  439.                 Result := Self.clients[index].BufferContentSize;
  440.         end;
  441. end;
  442.  
  443. function TTCPIPServerThread.BufferPointer(index: longint): pbyte;
  444. begin
  445.         if (index < Self.client_count) then
  446.         begin
  447.                 Result := Self.clients[index].Buffer;
  448.         end;
  449. end;
  450.  
  451. procedure TTCPIPServerThread.Send(index: longint; buf: pbyte; size: longword);
  452. begin
  453.         if (index < Self.client_count) then
  454.         begin
  455.                 Self.clients[index].OutBuf := buf;
  456.                 Self.clients[index].OutBufSize := size;
  457.         end;
  458. end;
  459.  
  460. function TTCPIPServerThread.OutBufferEmpty(index: longint): boolean;
  461. begin
  462.         if (index < Self.client_count) then
  463.         begin
  464.                 Result := Self.clients[index].OutBufSize = 0;
  465.         end;
  466. end;
  467.  
  468. procedure TTCPIPServerThread.FinishServer(HaltTime: longword);
  469. var
  470.         count: longword;
  471.         i: integer;
  472. begin
  473.         if (Self.Finished or Self.Terminated) then
  474.         begin
  475.                 exit;
  476.         end;
  477.  
  478.         for i := 0 to Self.client_count - 1 do
  479.         begin
  480.                 Self.clients[i].FinishClient(HaltTime);
  481.         end;
  482.         setlength(Self.clients, 0);
  483.         count := (HaltTime div 50) + 1;
  484.         Self.Terminate;
  485.         while ((not Self.Finished) and (count > 0)) do
  486.         begin
  487.                 fpnanosleep(@(Self.ts0), @(Self.ts1));
  488.                 dec(count);
  489.         end;
  490. end;
  491.  
  492. end.
  493.  

The weirdest part is, that the problem is gone now. In the other thread there was an fpnanosleep() with the interval of 1000000 nanosec. I replaced that to sleep(1). And now it works. Which is incomprehensible. At least for me.

TCH

  • Full Member
  • ***
  • Posts: 200
Re: impossible bug under ARM
« Reply #3 on: August 29, 2018, 12:22:28 pm »
It still trashes one variable in the same array, but now it is a different element, what is not affected right now, that's why the program worked in the morning.

How can a thread crash a variable in the main thread, during a third thread's fpselect?

Thaddy

  • Hero Member
  • *****
  • Posts: 14377
  • Sensorship about opinions does not belong here.
Re: impossible bug under ARM
« Reply #4 on: August 29, 2018, 12:22:35 pm »
No, the compiler gives no warnings. The linker gives the usual warning: "link.res contains output sections; did you forget -T?"
That means your FPC is old... From 3.0.4 that warning is gone...
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

Thaddy

  • Hero Member
  • *****
  • Posts: 14377
  • Sensorship about opinions does not belong here.
Re: impossible bug under ARM
« Reply #5 on: August 29, 2018, 12:28:27 pm »
The weirdest part is, that the problem is gone now. In the other thread there was an fpnanosleep() with the interval of 1000000 nanosec. I replaced that to sleep(1). And now it works. Which is incomprehensible. At least for me.
Not THAT weird: the requirements for fpnanosleep are rather specific and (as I wrote before) you should check for errors, which you didn't do (again) .
http://man7.org/linux/man-pages/man2/nanosleep.2.html
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

TCH

  • Full Member
  • ***
  • Posts: 200
Re: impossible bug under ARM
« Reply #6 on: August 29, 2018, 01:35:04 pm »
No, the compiler gives no warnings. The linker gives the usual warning: "link.res contains output sections; did you forget -T?"
That means your FPC is old... From 3.0.4 that warning is gone...
3.0.2. One version older than the current stable.
The weirdest part is, that the problem is gone now. In the other thread there was an fpnanosleep() with the interval of 1000000 nanosec. I replaced that to sleep(1). And now it works. Which is incomprehensible. At least for me.
Not THAT weird: the requirements for fpnanosleep are rather specific and (as I wrote before) you should check for errors, which you didn't do (again) .
http://man7.org/linux/man-pages/man2/nanosleep.2.html
There was a second post, which states, that it still trashes a variable in another thread. Also, i removed all of the fpnanosleeps from the project and the problem still persists.

TCH

  • Full Member
  • ***
  • Posts: 200
Re: impossible bug under ARM
« Reply #7 on: August 29, 2018, 01:59:22 pm »
I prevented the network event and the variable is still trashed by the other thread. So the TCP/IP server is innocent. But i cannot publish that thread as it is contains company related stuff.

Thaddy

  • Hero Member
  • *****
  • Posts: 14377
  • Sensorship about opinions does not belong here.
Re: impossible bug under ARM
« Reply #8 on: August 29, 2018, 03:33:49 pm »
Quote
3.0.2. One version older than the current stable.
yes, so you missed the version where that bogus warning was fixed..... :( :o :o :o
If a thread thrashes something it is the thread that is at fault and you should go over your code very carefully. This is not in anyway FPC or ARM related.
It may simply be that an Intel/amd is a little more forgiving with atomicity than arm is. Try to use interlocked* functions for writing to a shared var.
Note that I suspect that you will run into trouble on Intel/Amd too sooner or later. Those bugs will eventually manifest themselves.
Can you tell me the type and the size of the var that gets thrashed? Then I can either tell you operations on it can be assumed atomic or show you how to handle it.
Example: a 64 bit integer write is atomic on intel64 but not on Intel32 or arm32. On those you need either one of the interlocked functions or cannons like a TMultipleReadExclusiveWriteSynchronizer or a TCriticalSection.

(Oh, and did I mention to check for errors when using kernel/OS calls? < >:D>)
« Last Edit: August 29, 2018, 03:43:11 pm by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

TCH

  • Full Member
  • ***
  • Posts: 200
Re: impossible bug under ARM
« Reply #9 on: August 29, 2018, 04:00:42 pm »
You say, i ran into a compiler bug?

I did not touch that variable from anywhere! That variable is initialized in the beginning and then the stored values are loaded from config. After that nothing touches it. It is not a shared variable.

Also, my thread uses exactly zero shared variable. It stores everything in the thread and outsited the thread i get the results via getters.

Yes, i can tell the variable's structure:
Code: Pascal  [Select][+][-]
  1.         WR: array[0..3] of record
  2.                 RelayTime: longword;
  3.                 RelayDelayTime: longword;
  4.                 RelaySensorTime: longword;
  5.                 RelayNormalOpen: boolean;
  6.                 DirInWGID: byte;
  7.                 DirInUSBKBID: byte;
  8.                 DirOutWGID: byte;
  9.                 DirOutUSBKBID: byte;
  10.         end;
This variable is wrecked at some point. First it was WR[0].RelayTime, now it is WR[2].RelayDelayTime. It changed, when i have thrown out the fpnanosleep from the other thread. The variable is in the interface of another unit (namely the config). Neither of the two units has the other in the uses list.
« Last Edit: August 29, 2018, 04:04:44 pm by TCH »

Thaddy

  • Hero Member
  • *****
  • Posts: 14377
  • Sensorship about opinions does not belong here.
Re: impossible bug under ARM
« Reply #10 on: August 29, 2018, 04:44:19 pm »
Such a structure is NOT thread safe.
That definitely needs to be guarded with a critical section.

Is it? (That goes for any language, not just Pascal!)

Note my assessment was right: you were just lucky on Intel....
« Last Edit: August 29, 2018, 04:50:23 pm by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

TCH

  • Full Member
  • ***
  • Posts: 200
Re: impossible bug under ARM
« Reply #11 on: August 29, 2018, 05:00:07 pm »
Why? How can a thread ruin it which is not connected to it in any way? I would understand it if there would be some pointer operations, but there aren't. (Aside from the GPIO read/write, but if that would be erroneous, then the GPIO operations would not work.)

TCH

  • Full Member
  • ***
  • Posts: 200
Re: impossible bug under ARM
« Reply #12 on: August 29, 2018, 05:48:42 pm »
Okay, i think i found it. I made a checker which checked if the array differs from the original and inserted before and after all the relevant lines in the thread. And it wasn't the thread. It was the callback function in the main program. I forgot to check if the passed index is smaller than 9 so i overindexed a different array from a different unit. The only thing i don't understand, should not Pascal give a SEGFAULT then?

Thaddy

  • Hero Member
  • *****
  • Posts: 14377
  • Sensorship about opinions does not belong here.
Re: impossible bug under ARM
« Reply #13 on: August 29, 2018, 05:53:41 pm »
No, that depends. But did you use {$Rangechecks on} during debugging? Not!
Still, don't blame the callback too soon: that structure needs protection when it is used as an instance accessible to multiple threads..
« Last Edit: August 29, 2018, 06:01:42 pm by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

TCH

  • Full Member
  • ***
  • Posts: 200
Re: impossible bug under ARM
« Reply #14 on: August 29, 2018, 06:05:53 pm »
Well, i did not, but until this moment, whenever i made a similar mistake, i was always rewarded with a SIGSEGV. I just assumed, that it is how array indexing in Pascal works. Thanks for $Rangechecks tip.

I don't blame anything, the blame is mine. But the problem is solved. How can i protect the structure other than validating array indexes?

 

TinyPortal © 2005-2018