Recent

Author Topic: Hopeless Debugging  (Read 11724 times)

BLL

  • Sr. Member
  • ****
  • Posts: 276
Hopeless Debugging
« on: March 03, 2018, 08:46:13 pm »
Hi
I am getting an Access violation error message when I run my program.
If I run my program from within lazarus then it runs but doesn't see any of the USB devices which my program accesses (why not?), so it's useless in finding the problem! If I run the program outside lazarus, then I get the absolutely useless dialog that just tells me that an access violation has occurred but with no indication as to where it has happened! How the hell does one track down the error?

Talk about frustrating!!

Brian

RasPi3, jessie, fpc 3.1.1, lazarus 1.9.0
« Last Edit: March 03, 2018, 08:52:01 pm by BLL »

Thaddy

  • Hero Member
  • *****
  • Posts: 14213
  • Probably until I exterminate Putin.
Re: Hopeless Debugging
« Reply #1 on: March 03, 2018, 08:53:22 pm »
Brian, the only way is to provide code...?
Specialize a type, not a var.

jamie

  • Hero Member
  • *****
  • Posts: 6091
Re: Hopeless Debugging
« Reply #2 on: March 03, 2018, 09:04:25 pm »
Most likely an Error is developing at the OS level outside the debugger and is getting ignored somehow
.

 You need to employ some TRY USB code Except/Finally in the suspected areas.
The only true wisdom is knowing you know nothing

Bart

  • Hero Member
  • *****
  • Posts: 5275
    • Bart en Mariska's Webstek
Re: Hopeless Debugging
« Reply #3 on: March 04, 2018, 12:21:44 am »
Build with debuginfo (-gl) and see what the backtrace says when it crashes.
You might need a console for that, on Windows build with -WG- (Lazarus: Compiler options->Config and target: uncheck "Win32 gui application").

Bart

BLL

  • Sr. Member
  • ****
  • Posts: 276
Re: Hopeless Debugging
« Reply #4 on: March 04, 2018, 10:42:48 am »
Hi all
Thanks for the replies. Noone has explained why when running my program within lazarus, it can't communicate with any of its usb ports.

The -gl option sounds interesting, so I'll try that.

I have put try except loops all over the place, all to no avail. I have isolated the fault to one function but there's a lot of code in there! The access violation seems to have no pattern and can happen almost as soon as the program starts or it may be hours but it always happens!

Brian

RasPi3, jessie, fpc 3.1.1, lazarus 1.9.0

Thaddy

  • Hero Member
  • *****
  • Posts: 14213
  • Probably until I exterminate Putin.
Re: Hopeless Debugging
« Reply #5 on: March 04, 2018, 12:21:39 pm »
If you can locate it to a single procedure, did you resolve all hints and warnings?
Stackspace is dirty so any warning about uninitialzed vars should be resolved.
And dirty stackspace can easily sigsev on an uninitialized var or result.

Anyway: don't pollute your code with too many try/except, use assertions for that.
Specialize a type, not a var.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9794
  • Debugger - SynEdit - and more
    • wiki
Re: Hopeless Debugging
« Reply #6 on: March 04, 2018, 12:47:41 pm »
Thanks for the replies. Noone has explained why when running my program within lazarus, it can't communicate with any of its usb ports.
Unfortunately, no idea. Maybe it is because (as you say below) the error happens at random, and that affects the communication too? (Not too likely, but...)
Or a permission issue? Or environment?

You can start your app outside the IDE, and if it lasts long enough attach to it.
Or use gdbserver... maybe...

Quote
I have put try except loops all over the place, all to no avail. I have isolated the fault to one function but there's a lot of code in there! The access violation seems to have no pattern and can happen almost as soon as the program starts or it may be hours but it always happens!
If an error happens at random, you may have uninitialized variables....

Try compiling with
-gt
Also try with -gtt and -gttt and -gtttt

Best to add all of the following (and disable all optimizations)
-gt -gh -gl -O- -Criot -Sa

and set the following Environment variable (Run -> Run Parameters) / this will need a bit more memory, skip if you run out of mem.
HEAPTRC="keepreleased"

If anywhere your code is based on an assumption, such as Sender should contain an object of class TButton, then assert(Sender is TButton)

BLL

  • Sr. Member
  • ****
  • Posts: 276
Re: Hopeless Debugging
« Reply #7 on: March 05, 2018, 12:58:21 pm »
Hi all and thanks for all the replies.
I have this morning found the error and it was my fault!
One of the functions in my program uses data supplied by a PIC micro, which consists of a comma delimited list of electricity values (voltage, current, power etc) from sensors. The values are put into a Stringlist every few seconds and this function displays the results on screen. My code always checks that the list has the correct number of entries every time it is refreshed.
I then use the Val function on each item before using it, to make sure the stringlist item is a valid real number:
Code: Pascal  [Select][+][-]
  1. //Cost
  2.     Val(DataList.Strings[12], cost, ErrCode);
  3.     if ErrCode = 0 then
  4.       begin
  5.        if cost < 0 then cost := 0;
  6.        ElecForm.Cost.Caption := DataList.Strings[12];
  7.       end;
This has worked fine for months, but recently, I added some code lower in the function, which used DataList.Strings[12], but didn't use Val to check its validity, so although the above code was fine, it isn't now! I have now put that right and all seems well and it has been running fine now for some hours.

Thanks again folks.

Brian

BLL

  • Sr. Member
  • ****
  • Posts: 276
Re: Hopeless Debugging
« Reply #8 on: March 08, 2018, 06:49:16 pm »
Hi all
The bl**** error message has returned. Never in 38 years of programming have I had an error as obscure as this. The only concrete fact is that if I do not call the following function or any part of it, the error goes away:

Code: Pascal  [Select][+][-]
  1. [s][s]procedure TMainForm.doElecState;
  2. var V, I, P, Today, DAverage, solarData: String;
  3. current, voltage, power, excess, used_today, daily_average, cost, tmp, solarI: Real;
  4. tmp1, j, Chg:integer;
  5. LBV: real;
  6. gud: boolean;
  7.  begin
  8.  if(DataList.Count <> 16) then
  9.   exit;
  10.  
  11.   if Assigned(ElecForm) then
  12.    begin
  13.     V := DataList.Strings[0];
  14.     //spurious 1st char sometimes appears. If so, get rid of it!
  15.     if (Ord(V[1]) < 48) or (Ord(V[1]) > 57) then Delete(V, 1, 1);
  16.     Val(V, tmp, ErrCode);
  17.     if ErrCode <> 0 then exit
  18.     else
  19.      begin
  20.       voltage := 2 * (tmp - 200);
  21.       ElecForm.VoltBar.Progress := round(voltage);
  22.       ElecForm.VdigLabel.Caption := V + 'V';
  23.       ElecForm.VdigLabel.Repaint;
  24.      end;
  25.  
  26.     I := DataList.Strings[1];
  27.     Val(I, current, ErrCode);
  28.  
  29.     if ErrCode <> 0 then exit
  30.     else
  31.      begin
  32.       ElecForm.IdigLabel.Caption := I + 'A';
  33.       ElecForm.IdigLabel.Repaint;
  34.       //Set appropriate range for display
  35.       if current > 10.0 then
  36.        begin
  37.         ElecForm.IunitsLabel.Caption := HAScale;
  38.         ElecForm.CurrentBar.Progress := round(5 * current);
  39.        end
  40.       else
  41.        begin
  42.         ElecForm.IunitsLabel.Caption := LAScale;
  43.         ElecForm.CurrentBar.Progress := round(10 * current);
  44.        end;
  45.      end;
  46.     P := DataList.Strings[2];
  47.     Val(P, power, ErrCode);
  48.     if ErrCode <> 0 then exit
  49.     else
  50.      begin
  51.       if power > 1000 then
  52.        begin
  53.         ElecForm.PUnitsLabel.Caption := HPScale;
  54.         ElecForm.Label14.Caption := 'Power (kW):';
  55.         ElecForm.PowerBar.Progress := round(power/50);
  56.         ElecForm.PdigLabel.Caption := FloatToStrF(power/1000, ffFixed,2,2) + 'kW';
  57.        end
  58.       else
  59.        begin
  60.         ElecForm.PUnitsLabel.Caption := LPScale;
  61.         ElecForm.Label14.Caption := 'Power (W):';
  62.         ElecForm.PowerBar.Progress := round(power / 10);
  63.         ElecForm.PdigLabel.Caption := P + 'W';
  64.        end;
  65.      end;
  66.     //add values to StatusList
  67.    { StatusList.Strings[13] :=  ls + 'V = ' + ElecForm.VdigLabel.Caption + ', I = '
  68.           + ElecForm.IdigLabel.Caption + ', P = ' + ElecForm.PdigLabel.Caption + le; }
  69.     //Used today
  70.     Today := DataList.Strings[10];
  71.     Val(Today, used_today, ErrCode);
  72.     if ErrCode <> 0 then exit
  73.     else
  74.      if ErrCode = 0 then
  75.       begin
  76.       if(used_today >= 0) and (used_today < 100) then
  77.        begin
  78.         ElecForm.TodayBar.Progress := round(used_today * 10);
  79.         ElecForm.TdigLabel.Caption := Today + 'kWh';
  80.         StatusList.Strings[14] := ls + 'Used today = ' + ElecForm.TdigLabel.Caption + le;
  81.         ElecForm.TdigLabel.Repaint;
  82.        end;
  83.       if(used_today > 6.0) then ElecForm.TodayBar.ForeColor  := clRed
  84.       else
  85.        ElecForm.TodayBar.ForeColor := clLime;
  86.      end;
  87.  
  88.     //daily average
  89.     DAverage := DataList.Strings[9];
  90.     Val(DAverage, daily_average, ErrCode);
  91.     if ErrCode <> 0 then exit
  92.     else
  93.      begin
  94.       if(daily_average < 20) then
  95.        begin
  96.         ElecForm.DAvg.Progress := round(daily_average * 5);
  97.         ElecForm.DAdigLabel.Caption := DAverage + 'kWh';
  98.         //StatusList.Strings[15] := ls + 'Daily Avg = ' + ElecForm.DAdigLabel.Caption + le;
  99.        end;
  100.  
  101.       if(daily_average > 6) then ElecForm.DAvg.ForeColor := clRed
  102.       else
  103.        ElecForm.DAvg.ForeColor := clLime;
  104.      end;
  105.  
  106.     //Excess units
  107.     Val(DataList.Strings[11], excess, ErrCode);
  108.     if ErrCode <> 0 then exit
  109.     else
  110.      begin
  111.       if(excess < 0) then excess := 0;
  112.        ElecForm.ExcessUnits.Caption := FloatToStrF(excess, ffFixed,3,1)
  113.         + 'kWh';
  114.      end;
  115.  
  116.     //Currency unit
  117.     if not Assigned(setupform) then exit;
  118.     if curSymbol = '' then
  119.      begin
  120.       Val(setupData.Strings[3], tmp1, ErrCode);
  121.       if ErrCode <> 0 then exit
  122.       else
  123.        begin
  124.         if tmp1 = 1 then
  125.          curSymbol := '€'
  126.        else
  127.         curSymbol := '£';
  128.        end;
  129.      end;
  130.  
  131.     //Cost
  132.     Val(DataList.Strings[12], cost, ErrCode);
  133.     if ErrCode <> 0 then exit
  134.     else
  135.      begin
  136.       if cost < 0 then cost := 0;
  137.       ElecForm.Cost.Caption := DataList.Strings[12];
  138.      //end;
  139.     //StatusList.Strings[16] := '';
  140.    { if CurSymbol = '£' then
  141.      StatusList.Strings[16] := ls + 'Cost = ' + '£'
  142.          + DataList.Strings[12] + le
  143.     else
  144.      StatusList.Strings[16] := ls + 'Cost = ' + DataList.Strings[12]
  145.         + '€' + le; }
  146.      ElecForm.Cost.Caption := '';
  147.      if CurSymbol = '€' then
  148.       ElecForm.Cost.Caption := 'Cost = ' + DataList.Strings[12] + CurSymbol
  149.      else
  150.       ElecForm.Cost.Caption := 'Cost = ' + CurSymbol + DataList.Strings[12];
  151.      end;
  152.  
  153.      //process LBV, ChgI
  154.        for j := 0 to 5 do
  155.         begin
  156.          gud := false;
  157.          //read SOLAR battery voltage value
  158.          solarData := srData('SOLAR', '12548'); //0x3104
  159.          //get rid of CR at end of string
  160.          if Length(solarData) > 1 then
  161.           SetLength(solarData, Length(solarData) - 1);
  162.          Val(solarData, LBV, ErrCode);
  163.          if ErrCode = 0 then
  164.           begin
  165.            gud := true;
  166.            break;
  167.           end;
  168.         end;
  169.        //final check in case all 5 read attempts failed
  170.        if gud = false then exit
  171.        else
  172.         begin
  173.          LBV := LBV/100; //mV to V
  174.          ElecForm.LBVValue.Caption := FloatToStrF(LBV, ffFixed,3,2) + 'V';
  175.          {StatusList.Strings[18] := ls + 'Pri Bat = '
  176.             + ElecForm.LBVValue.Caption + ' Sec = ' + le;}
  177.          //offset as scale starts at 8V
  178.          LBV := LBV - 8;
  179.          if LBV < 0 then LBV := 0;
  180.          ElecForm.LBVBar.Progress := round(LBV * 10);
  181.         end;
  182.  
  183.        //Battery charge percentage
  184.        for j := 0 to 5 do
  185.         begin
  186.          gud := false;
  187.          solarData := srData('SOLAR', '12570'); //0x311A
  188.         if Length(solarData) > 1 then
  189.           SetLength(solarData, Length(solarData) - 1);
  190.        //
  191.         Val(solarData, Chg, ErrCode);
  192.         if ErrCode = 0 then
  193.          begin
  194.           gud := true;
  195.           break;
  196.          end;
  197.         end;
  198.  
  199.        if gud = false then exit
  200.        else
  201.         begin
  202.          if Chg > 25 then ElecForm.BatState.ForeColor := clLime
  203.          else ElecForm.BatState.ForeColor := clRed;
  204.          ElecForm.BatState.Progress := Chg;
  205.         end;
  206.  
  207.      //LBI
  208.      Val(DataList.Strings[6], LBI, ErrCode);
  209.      if ErrCode <> 0 then exit
  210.      else
  211.       begin
  212.        LBI := LBI - 499;
  213.        if LBI < 0 then LBI := 0;
  214.        ElecForm.ChgI.Progress := round (LBI/5.843);
  215.        ElecForm.LBIValue.Caption := FloatToStrF(LBI/10.5, ffFixed,3,2) + 'A';
  216.       end;
  217.  
  218.       //solar charge current
  219.        for j := 0 to 5 do
  220.         begin
  221.          gud := false;
  222.          solarData := srData('SOLAR', '12549'); //0x3105
  223.          //get rid of CR at end of string, only if string isn't empty
  224.          //or an Access violation occurs
  225.          if Length(solarData) > 1 then
  226.           SetLength(solarData, Length(solarData) - 1);
  227.          Val(solarData, solarI, ErrCode);
  228.          if ErrCode = 0 then
  229.           begin
  230.            gud := true;
  231.            break;
  232.           end;
  233.         end;
  234.       if gud = false then exit
  235.       else
  236.        begin
  237.         //mA to A
  238.         if solarI < 0 then solarI := 0;
  239.         solarI := solarI/100;
  240.         ElecForm.SolarIval.Caption  := FloatToStrF(solarI, ffFixed,3,2) + 'A';
  241.         ElecForm.SolarBar.Progress := 2 * round(solarI);
  242.        end;
  243.     {StatusList.Strings[19] := ls + 'Load = ' + ElecForm.LBIValue.Caption
  244.         + ', Mchg = ???A, ' + 'Schg = ' + ElecForm.SolarIval.Caption + le;}
  245.    end;
  246.   end;[/s][/s]
I have remmed out all calls to StatusList, which is supposed to write data to the stringlist to save to a file which the webserver will send if requested.

It seems that I need to use the options -g -gl -gs -gw and -Xg and do a backtrace, but I have never done this before. I have looked in project options but most of these switches aren't there. Do I have to compile from the command line instead of from lazarus? If so, I don't have a clue how to do that. Then, how do I access this debug info and use it?

I am just so fed up with it!!

Brian

Josh

  • Hero Member
  • *****
  • Posts: 1271
Re: Hopeless Debugging
« Reply #9 on: March 08, 2018, 07:40:20 pm »
Hi

Just a thought, what would happen if V contained just one spurious character?
V would be Empty, could this cause your error.
Or if V was an Empty String to start with.

I have also stopped using STRING and then indexing it for a character within it. I tend to use RawByteString; due to Unicode changes.


http://wiki.freepascal.org/not_Delphi_compatible_enhancement_for_Unicode_Support



Code: [Select]
V := DataList.Strings[0];
 //spurious 1st char sometimes appears. If so, get rid of it!
 if (Ord(V[1]) < 48) or (Ord(V[1]) > 57) then Delete(V, 1, 1);
 Val(V, tmp, ErrCode);
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9794
  • Debugger - SynEdit - and more
    • wiki
Re: Hopeless Debugging
« Reply #10 on: March 08, 2018, 08:53:04 pm »
I am assuming that you are again unlucky, and you do not get the error if your run this in the debugger?

First thing you can do to narrow it down:
insert lots of
Code: Pascal  [Select][+][-]
  1. debugln('I am at line ....');
You can use writeln instead of debugln. debugln is part of the LazLogger.
You can also output the content of variables.

Then compile with console (project options / app-type: uncheck GUI), and watch the output.

2nd thing:
Again asserts.
Quote
Code: Pascal  [Select][+][-]
  1. if(DataList.Count <> 16) then
Is DataList assigned?
Code: Pascal  [Select][+][-]
  1. assert(DataList<>nil, 'error DataList is nil')
I haven't read the entire bit of code not sure what else there may be...

3rd
"if assigned" or "<> nil) (including my assert above)

Those are not a gurantee, that the variable can be used.
Code: Pascal  [Select][+][-]
  1. a := TFoo.create;
  2. a.destroy;
a is assigned, but it will still crash....


Cyrax

  • Hero Member
  • *****
  • Posts: 836
Re: Hopeless Debugging
« Reply #11 on: March 08, 2018, 09:12:26 pm »
Can you show us the code where you are reading values from USB? It sounds like that you are somehow writing the values from USB to string index of 0 which is big no no.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9794
  • Debugger - SynEdit - and more
    • wiki
Re: Hopeless Debugging
« Reply #12 on: March 08, 2018, 11:09:23 pm »
Quote
It seems that I need to use the options -g -gl -gs -gw and -Xg and do a backtrace, but I have never done this before. I have looked in project options but most of these switches aren't there. Do I have to compile from the command line instead of from lazarus? If so, I don't have a clue how to do that. Then, how do I access this debug info and use it?
You can always put them in "Project Options" >  "Custom Options" (3rd last). But you also find them on the "debugging" page of Project Opts.

----

-gl is will give you a backtrace to stdout/console (if you compile with console enabled), otherwise to a logfile if you redirect.

But only if your exception is caught, and something calls DumpStack or similar. For normal exceptions, if you do not catch them yourself, the LCL may do that for you (IIRC).

But access violations are a tricky beast.
They are not a exception (in the sense of raise Exception.create()...)
They are a signal.

access violations  means the OS has detected that your app tried to access memory to which it has no rights.
So the OS sends the signal, and the app has to catch it and convert it to an exception.
Only it may not be able to do so. An access violations almost always means that your apps memory got corrupted. So anything your app tries to do can cause further errors. Sometimes even all stack trace info is destroyed..... Yet sometimes it can be caught.

The best would be if you can get the error in the Debugger.
For that you should use  -gw
"Project Options" >  "Debugging"
 Generate debugging info for GDB
 Type of debug info: Dwarf with sets

http://wiki.lazarus.freepascal.org/Debugger_Setup

------------
On "Project Options" >  "Debugging" you may also want to enable
"Checks and Assertion" : ALL (you may skip "Verify method calls"
"Trash variables"
"Use Heaptrc"

BLL

  • Sr. Member
  • ****
  • Posts: 276
Re: Hopeless Debugging
« Reply #13 on: March 08, 2018, 11:30:10 pm »
Wow - that's a lot to take in!! To read from USB devices, I originally tried to use sdpoSerial and found it absolutely useless and unreliable so I now call a small C program which receives parameters from my lazarus app, sends them to the converter. Data returned is printed to screen, but TProgress sends it to a string. I am using this on a number of USB-serial converters with no problems. Here is the lazarus code:
Code: Pascal  [Select][+][-]
  1. function TMainForm.srData(prog, args : String) : String;
  2. var
  3.  p:TProcess;
  4.  list:TstringList;
  5. begin
  6. list := TStringList.Create;
  7. p := TProcess.Create(nil);
  8. list.Sorted := false;
  9. list.StrictDelimiter := true;
  10. list.Delimiter := #10;
  11. //none of the following need progPath
  12. if ((prog = 'date') or (prog = 'hwclock') or (prog = 'lxterminal') or (prog = 'chromium-browser')
  13.   or (prog = 'rm')) then
  14.  p.Executable := prog
  15. else
  16.  p.Executable := progPath + prog;
  17.  
  18. p.Parameters.Add(args);
  19. p.Options := p.Options + [poWaitOnExit, poUsePipes];
  20. p.Execute;
  21. list.LoadFromStream(p.Output);
  22. Result := list.Text;
  23. FreeAndNil(list);
  24. FreeAndNil(p);
  25. end;
The C program looks like this:
Code: Pascal  [Select][+][-]
  1. //Get data from /send data to PIC
  2. //usage: ./PIC "#"
  3.  
  4. #include <string.h>
  5. #include <errno.h>
  6. #include <stdio.h>
  7. #include <wiringSerial.h>
  8. #include <unistd.h>
  9. #include <time.h>
  10.  
  11. void sleep_ms(int);
  12.  
  13. int main(int argc, char **argv)
  14. {
  15. int fd, i, count;
  16. if((fd = serialOpen("/dev/PIC", 115200)) < 0)
  17.   {
  18.     fprintf(stdout, "Unable to open serial device: %s\n", strerror(errno));
  19.     return 1;
  20.   }
  21. serialPrintf(fd, argv[1]);
  22. serialPrintf(fd, "\r\n");
  23. sleep_ms(500);
  24. count = serialDataAvail(fd);
  25. //Loop, getting and printing characters
  26.  
  27. for(i = 0; i < count; i++)
  28.  {
  29.   putchar(serialGetchar(fd));
  30.   fflush(stdout);
  31.  }
  32. serialClose(fd);
  33. return 0;
  34. }
  35. //--------------------------------------------------------------------------------------
  36. void sleep_ms(int milliseconds) // cross-platform sleep function
  37. {
  38.  struct timespec ts;
  39.  ts.tv_sec = milliseconds / 1000;
  40.  ts.tv_nsec = (milliseconds % 1000) * 1000000;
  41.  nanosleep(&ts, NULL);
  42. }

I will try the other suggestions.

Brian

jamie

  • Hero Member
  • *****
  • Posts: 6091
Re: Hopeless Debugging
« Reply #14 on: March 09, 2018, 01:07:56 am »
quick glance I would say its a fail!

 You are using Shell shortcuts..

 Tprocess I am sure requires the actual path to the file...

 Try using "ShellExecute" if you want to launch programs like that.

Also, have you looked for Serial support units ? They are out there and they work.
The only true wisdom is knowing you know nothing

 

TinyPortal © 2005-2018