Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Zeilennummer des Quellcodes ermitteln


galagher - Mi 22.01.14 21:12
Titel: Zeilennummer des Quellcodes ermitteln
Hallo!

Mein Programm schreibt Einträge in eine Log-Datei. Kennt jemand eine Möglichkeit, Zeilennummern des Quellcodes zu ermitteln? Die hätte ich nämlich auch gerne in der Log-Datei aufgelistet, ich möchte dazu aber natürlich keine fixen Werte verwenden!


Martok - Mi 22.01.14 22:00

Ohne Debuginfo nicht (falls nicht in den letzten Delphis was gekommen ist via RTTI-Erweiterungen). Soweit ich weiß, gibt's aber nicht sowas schönes wie das __LINE__-Macro...

Mit Debuginfo hätte ich das nur für FPC vorrätig...


jaenicke - Mi 22.01.14 22:00

Einen Stacktrace bekommst du mit der JEDI Code Library. Genauer gesagt mit der JclDebug Unit.
In jedem Fall brauchst du dafür die nötigen Debuginformationen, sprich deine Exe wird relativ groß werden.

Die einzige andere Möglichkeit sind Assertions, die dies direkt beim Kompilieren einbauen.

Beides ist allerdings für ein normales Logging nicht so sinnvoll, es sei denn es geht um aufgetretene Fehler.


galagher - Do 23.01.14 22:00

Vielen Dank für eure Antworten!

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Die einzige andere Möglichkeit sind Assertions, die dies direkt beim Kompilieren einbauen.
Kannst du mir dazu ein Beispiel oder einen Tipp geben?

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Beides ist allerdings für ein normales Logging nicht so sinnvoll, es sei denn es geht um aufgetretene Fehler.
Nein, es geht um keine Fehler, möchte das nur zur Info!


jaenicke - Do 23.01.14 23:50

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Die einzige andere Möglichkeit sind Assertions, die dies direkt beim Kompilieren einbauen.
Kannst du mir dazu ein Beispiel oder einen Tipp geben?
Einfach Assert aufrufen. Wenn der angegebene Ausdruck false ergibt, wird eine Exception ausgelöst, in der die Position im Quelltext steht.


galagher - Fr 24.01.14 17:57

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Einfach Assert aufrufen. Wenn der angegebene Ausdruck false ergibt, wird eine Exception ausgelöst, in der die Position im Quelltext steht.
Ich werde das mal testweise einbauen (heute noch oder auch erst am Wochenende, weiss noch nicht) und melde mich, wenn nötig, wieder!


Palladin007 - Fr 24.01.14 19:46

Ich habe heute etwas von einem CallerMemberNameAttribute gelesen, das ab C# 5.0 dabei sein soll.
Damit kannst du dir genau das auslesen lassen.


Außerdem kann man aktuell schon über ein kostenloses AddIn und später vielleicht auch vollständig implementiert, einen Compiler nutzen, der es erlaubt, über Code die einzelnen Arbeitsschritte zu analysieren und zu manipulieren.

Den Genauen Namen weiß ich aber nicht mehr...


galagher - Fr 24.01.14 20:02

user profile iconPalladin007 hat folgendes geschrieben Zum zitierten Posting springen:
Ich habe heute etwas von einem CallerMemberNameAttribute gelesen,
Ich möchte nichts installieren oder einbinden oder mich da grossartig einarbeiten müssen, so wichtig ist mir das nun doch wieder nicht!

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Einfach Assert aufrufen. Wenn der angegebene Ausdruck false ergibt, wird eine Exception ausgelöst, in der die Position im Quelltext steht.
Assert(1=0); Das erzeugt eine Exception, aber wie komme ich da an die Zeilennummer?


jaenicke - Fr 24.01.14 20:55

Unitname und Zeilennummer stehen doch im Exceptiontext drin.


galagher - Fr 24.01.14 21:30

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Unitname und Zeilennummer stehen doch im Exceptiontext drin.
Ich habe eine Prozedur ApplicationEvents1Exception, die wird dann natürlich aufgerufen, ja, und dann wird dort auch der Fehlertext samt Zeilennummer in die Logdatei geschrieben. Es werden aber an vielen Stellen Logeinträge vorgenommen, die keinen Fehler betreffen.

Ausserdem gefällt mir der Gedanke nicht, einen Fehler zu produzieren(!), nur um an die Zeilennummer zu kommen. Das ist ja so, wie mit seinem Auto gegen die Wand zu fahren, weil man den Airbag sehen will.
Ich will den Airbag aber ohne Crash sehen!

Geht wohl mit Delphi nicht...


galagher - Sa 25.01.14 21:41

Es gibt ein Beispiel in der Hilfe von Delphi 6, damit habe ich mir etwas gebastelt, das für meine Zwecke ausreicht. Und jetzt die Preisfrage: Ich muss dazu rund 150 Mal Assert aufrufen, um an die jeweilige Zeilennummer zu kommen. Gibt es einen Weg, Assert in einer Prozedur nur 1 Mal zu haben, und trotzdem an die Zeile zu kommen, von der diese Prozedur aufgerufen wurde? In anderen Fällen würde man das so machen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
if i <> 0 then
  MeineProzedur;  //Die Nummer dieser Zeile möchte ich...

procedure TForm1.MeineProzedur;
begin
  Assert(0=1);  //... da haben!

  //...
end;


jaenicke - Sa 25.01.14 21:50

Das wäre wiederum ein Fall für einen Stacktrace. Das geht nicht, nein.

Eigentlich frage ich mich halt wofür du das eigentlich brauchst. Wenn es bei einem Log darum geht, einen unbekannten Fehler abzufangen, dann fange ich die Exception zentral ab und logge diese zentral automatisch mit Stacktrace, letzten Anwendungsaktionen, usw.
Wenn ich Logeinträge schreibe um einen Ablauf nachzuvollziehen, muss ich die aber ohnehin an konkrete Stellen schreiben. Da muss ich ja, damit es sinnvoll ist, ohnehin Informationen zum Ablauf hinzufügen. Und dann brauche ich die Quelltextstelle nicht mehr, weil es ohnehin eindeutig ist wo das auftritt.

Wenn du einfach nur einen kopierten Befehl überall als Log einstreuen willst ohne den zu ändern (halte ich für wenig sinnvoll, aber deine Sache), dann bleibt dir nur ein Stacktrace. Der ist zwar nicht ganz so schnell erstellt, aber ein ungezieltes Logging ist ja auch nicht gerade ressourcenschonend, insofern ist das ja für dich vielleicht ok...


galagher - Sa 25.01.14 21:57

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Das geht nicht, nein.
Dachte ich mir. Dann lasse ich das. Immerhin habe ich nun eine Datei, mit der das prinzipiell geht!


galagher - So 26.01.14 19:19

Letzter Anlauf:

Ich dachte daran, Assert an eine Prozedur als Parameter zu übergeben. Habe auch gegoogelt, es gibt einiges zu dem Thema, aber nichts, was für meine Zwecke geeignet ist.

Ich denke mir das so:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
procedure TForm1.MeineProzedur(... {diverse Parameter});  //was muss hier stehen, damit ich...
begin
//...
end;

MeineProzedur(Assert(0=1), {weitere Parameter});  //... -> das hier machen kann?


Ich schaffe es einfach nicht! Geht das überhaupt?


Thom - So 26.01.14 22:57

Die globale Variable AssertErrorProc kann mit einer eigenen Prozedur initialisiert werden:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
procedure MyAssertErrorProc(const Message, Filename: String; LineNumber: Integer; ErrorAddr: Pointer);
begin
  ShowMessage(IntToStr(LineNumber));
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  AssertErrorProc:=MyAssertErrorProc;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Assert(false);
end;


Zusätzliche Parameter könntest Du dabei als String im Parameter Message von Assert übergeben:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure MyAssertErrorProc(const Message, Filename: String; LineNumber: Integer; ErrorAddr: Pointer);
var
  Params: TStringList;
begin
  Params:=TStringList.Create;
  try
    Params.CommaText:=Message;
    Params.Add('Zeile: '+IntToStr(LineNumber));
    ShowMessage(Params.Text);
  finally
    Params.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Assert(false,Format('%s,%d',['Parameter_1',10]));
end;


galagher - Mo 27.01.14 18:47

user profile iconThom hat folgendes geschrieben Zum zitierten Posting springen:
Die globale Variable AssertErrorProc kann mit einer eigenen Prozedur initialisiert werden:
Ja, so in der Art sieht meine pas-Datei aus, die ich anhand der Delphi-Hilfe geschrieben habe.

user profile iconThom hat folgendes geschrieben Zum zitierten Posting springen:


Delphi-Quelltext
1:
2:
3:
4:
procedure TForm1.Button1Click(Sender: TObject);
begin
  Assert(false,Format('%s,%d',['Parameter_1',10]));
end;
Nur muss ich da eben Assert immer noch extra aufrufen!

Meine Frage war: Kann man Assert (und damit Prozeduren und Funktionen generell) als Parameter an eine Prozedur übergeben? Also so:

Delphi-Quelltext
1:
2:
3:
4:
procedure TForm1.Button1Click(Sender: TObject);
begin
  MyProc(Assert(0=1), ...);
end;


Thom - Mo 27.01.14 19:27

Selbstverständlich kann man Methoden, Prozeduren und Funktionen als Parameter übergeben. Aber


Delphi-Quelltext
1:
2:
3:
4:
procedure TForm1.Button1Click(Sender: TObject);
begin
  MyProc(Assert(0=1), ...);
end;

funktioniert in dieser Form nicht, da hier nicht die Prozedur Assert selbst übergeben wird, sondern das Ergebnis des Aufrufs. Das geht aber nicht, da Assert eine Prozedur und keine Funktion ist. Um also auf Deine Frage klar zu antworten: Nein - so geht das nicht. Weder mit Assert noch mit irgend einer anderen Delphi-Prozedur.
Erschwerend kommt noch hinzu, daß bei Assert "Kompilermagie" im Spiel ist. Beim Kompilieren werden nämlich der Aufruf und die Parameter so umgeformt, daß im Endeffekt die Prozedur System._Assert ausgeführt wird.

Für Dich ist es kein Problem MyProc(Assert(0=1), ...); zu schreiben, willst aber Assert nicht aufrufen:

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Nur muss ich da eben Assert immer noch extra aufrufen!

Tut mir Leid, aber ich verstehe Dein Problem nicht.
Leitest Du den Assert-Aufruf in eine eigene Prozedur um, erhältst Du die von Dir gewünschten Angaben ohne Exception frei Haus. Aber offensichtlich willst Du das doch nicht... :gruebel:


galagher - Mo 27.01.14 19:49

user profile iconThom hat folgendes geschrieben Zum zitierten Posting springen:

Für Dich ist es kein Problem MyProc(Assert(0=1), ...); zu schreiben, willst aber Assert nicht aufrufen:

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Nur muss ich da eben Assert immer noch extra aufrufen!

Tut mir Leid, aber ich verstehe Dein Problem nicht.
Leitest Du den Assert-Aufruf in eine eigene Prozedur um, erhältst Du die von Dir gewünschten Angaben ohne Exception frei Haus. Aber offensichtlich willst Du das doch nicht... :gruebel:

Ok, ich habe mich wohl unklar ausgedrückt! Ich rufe in meiner Unit an 150 verschiedenen Stellen eine Prozedur auf, die unter anderem etwas in eine TStringList schreibt, welche ich bei Programmende dann als Datei mit der Endung .log speichere.

Vor jeden dieser über 150 Prozeduraufrufe müsste ich Assert setzen. Also konkret so:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
Assert(0=1);
TextOutPut(s, ...);
//...
begin
  if ... then
  begin
    Assert(0=1);
    TextOutPut(s, ...);
  end;
//...
begin
  Assert(0=1);
  TextOutPut(s, ...);
end;
//...
end;


Da wäre es doch so viel einfacher:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
TextOutPut(Assert(0=1), s, ...);
//...
begin
  if ... then
    TextOutPut(Assert(0=1), s, ...);
//...
begin
  TextOutPut(Assert(0=1), s, ...);
end;
//...
end;

Weisst du, was ich meine? Assert einfach als Parameter übergeben und:

Delphi-Quelltext
1:
2:
3:
4:
procedure MyAssertErrorProc(const Message, Filename: String; LineNumber: Integer; ErrorAddr: Pointer);
begin
  ShowMessage(IntToStr(LineNumber));
end;

... kann ich dann auch verwenden, da das ja, wie du richtig sagst, genau das tut, was ich möchte: Mir die LineNumber geben!
Aber wenn's nunmal nicht geht, ok!


GuaAck - Mo 27.01.14 19:57

Mal ein anderer Ansatz, so wie ich es mache:

Ich definiere einfach eigene Kennnummern hard-coded im Quelltext, also z. B.:

writeln(logfile, '100 Hier dies...');
...
writeln(logfile, '110 hier das...');
...
writeln(logfile, '120 und das hier...');

Gegenüber Zeilennumern hat das den wesentlichen Vorteil, dass meine Kennnummern unabhängig davon sind, ob ich irgendwo anders Code einfüge, ich kann sie also in meine Dokumentation übernehmen.

Logisch, statt 100, 110,120 kann man auch noch den Modulnamen usw. einbauen und bei Klassen evtl. den Namen der jeweiligen Instanz. Da man ein Logging dieser Art ja eigentlich nur für eine Fehlersuche nutzt, reichen auch die Nummern, mit denen man ganz schnell im Editor die entsprechende Stelle suchen kann.

Oder habe ich da im Prinzip was nicht richtig verstanden?

(Bevor sich einer über writeln aufregt: Steht sinngemäß für jede Art, etwas in den Logfile zu schreiben.)

Grüße
GuaAck


galagher - Mo 27.01.14 20:11

user profile iconGuaAck hat folgendes geschrieben Zum zitierten Posting springen:
Ich definiere einfach eigene Kennnummern hard-coded im Quelltext,
Ja, so mache ich das jetzt auch. Alerdings verwende ich mehrere Strings zusammen mit diesen Kennnummern, ach, was red ich - so:


Delphi-Quelltext
1:
2:
3:
4:
5:
TextOutPut(s, sHuman, '01 '+sSonstige, True);
//..
TextOutPut(s, sHuman, '02 '+sSonstige, False);
//..
TextOutPut(s, sHuman, '01 '+sUserName, True);
Also kommt 01 an mehreren Stellen vor, das geht zum Teil bis 70. Das zu verwalten und korrekt fortzuführen, wenn an Code etwas dazukommt oder wegfällt, ist nicht einfach. Und das im Moment über 150 Mal! Es wäre besser mit Zeilennummern!

Nun interessiert mich einfach, ob eine Parameterübergabe so überhaupt möglich ist. Naja, wohl nicht. :(


Thom - Mo 27.01.14 21:30

Nein - das geht wirklich nicht. Wenn in der Zeile Assert(false); steht, ermittelt und übergibt der Kompiler automatisch die Zeilennummer. Willst Du das nicht, mußt Du das manuell machen und bei jeder Quelltextveränderung anpassen (was bei 150 Stellen sicher wenig Spaß macht).
Ist es aber nicht egal, ob Du Assert in eine Zeile schreibst und den Aufruf der Log-Funktion in die nächste oder alles in eine Zeile? Notfalls würde ja auch


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
var
  CurrentLineNumber: Integer;

procedure MyAssertErrorProc(const Message, Filename: String; LineNumber: Integer; ErrorAddr: Pointer);
begin
  CurrentLineNumber:=LineNumber;
end;
 
[...]
  if ... then
    begin Assert(false); TextOutPut(CurrentLineNumber, s, ...); end;
[...]

statt


Delphi-Quelltext
1:
2:
 if ... then
    TextOutPut(Assert(0=1), s, ...);

gehen. Oder? Funktioniert natürlich nur, wenn Assert aus dem Hauptthread heraus aufgerufen wird (wegen der globalen Variable).


galagher - Mo 27.01.14 22:17

user profile iconThom hat folgendes geschrieben Zum zitierten Posting springen:
Ist es aber nicht egal, ob Du Assert in eine Zeile schreibst und den Aufruf der Log-Funktion in die nächste oder alles in eine Zeile?
Ich ärgere mich ja jetzt über mich selbst, denn ich hatte den Code schon komplett geändert mit Assert, hat auch prima funktioniert. Aber weil es mir nicht gefiel, 150x Assert aufzurufen, habe ich alles wieder rückgängig gemacht (nicht manuell, habe einfach wieder die alte Version der .pas-Datei verwendet). :autsch: Also nochmal? Überlegenswert ist es... :mrgreen:


Blup - Di 28.01.14 11:53

Für den Anwender ist so ein Log mit Zeilennummern sicher nicht sinnvoll.
Da sollte nur stehn welche Funktion ausgeführt wurde und mit welchem Ergebnis.

Für unbekannte Fehler kann man Tools wie "Eurekalog" einsetzen.
Dieses liefert im Fehlerfall ein Protokoll mit kompletten Aufrufstapel und Zeilennummern.

Dann gibts da noch die sogenannten "Profiler".
Diese arbeiten als eine Art Precompiler und fügen für jede überwachte Methode automatisch Code ein.
Dieser protokolliert den Aufruf und das Verlassen der Methode, die Zeit usw..


galagher - Di 28.01.14 21:03

user profile iconBlup hat folgendes geschrieben Zum zitierten Posting springen:
Für den Anwender ist so ein Log mit Zeilennummern sicher nicht sinnvoll.
Der Anwender bin ich!

Habe jetzt den Code konsequent mit Assert umgestellt, Assert steht zusammen mit dem entsprechenden Code, auf den es sich bezieht, in einer Zeile. Also habe ich jetzt zwei Anweisungen in einer Zeile stehen (pfui!), aber ist halt so. Umschlossen mit teilweise sinnlosem begin - end. Zwar ist es so wenigstens einheitlich, aber eben einheitlich pfui. :puke:

Wie herrlich wäre doch die (Delphi)-Welt, wenn man an Prozeduren/Funktionen einfach Prozeduren/Funktionen als Parameter übergeben könnte - uneingeschränkt, versteht sich!


jaenicke - Di 28.01.14 21:27

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Wie herrlich wäre doch die (Delphi)-Welt, wenn man an Prozeduren/Funktionen einfach Prozeduren/Funktionen als Parameter übergeben könnte - uneingeschränkt, versteht sich!
Naja, theoretisch...

Delphi-Quelltext
1:
LogCurrent(procedure begin Assert(False) end'Logstring');                    
Das geht ja durchaus...

Also vollständig:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
type
  EAssertionFailedEx = class(EAssertionFailed)
  private
    FFilename: string;
    FMessage: string;
    FLineNumber: Integer;
    FErrorAddr: Pointer;
  public
    constructor Create(const AMessage, AFilename: string; ALineNumber: Integer; AErrorAddr: Pointer);
    property Messagestring read FMessage write FMessage;
    property Filename: string read FFilename write FFilename;
    property LineNumber: Integer read FLineNumber write FLineNumber;
    property ErrorAddr: Pointer read FErrorAddr write FErrorAddr;
  end;

{ EAssertionFailedEx }

constructor EAssertionFailedEx.Create(const AMessage, AFilename: string; ALineNumber: Integer; AErrorAddr: Pointer);
begin
  inherited Create(AMessage);
  FMessage := AMessage;
  FFilename := AFilename;
  FLineNumber := ALineNumber;
  FErrorAddr := AErrorAddr;
end;

procedure AssertionFailed(const Message, Filename: string; LineNumber: Integer; ErrorAddr: Pointer);
begin
  raise EAssertionFailedEx.Create(Message, Filename, LineNumber, ErrorAddr);
end;

procedure LogCurrent(const AAssertProcedure: TProc; const ALogText: String);
begin
  try
    AAssertProcedure;
  except
    on E: EAssertionFailedEx do
      ShowMessage('Logeintrag:'#13#10 + E.Message + #13#10 + E.Filename + #13#10 + IntToStr(E.LineNumber));
  end;
end;

procedure TForm53.FormCreate(Sender: TObject);
begin
  AssertErrorProc := AssertionFailed;
  LogCurrent(procedure begin Assert(False) end'Logstring');
end;


galagher - Di 28.01.14 21:57

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Wie herrlich wäre doch die (Delphi)-Welt, wenn man an Prozeduren/Funktionen einfach Prozeduren/Funktionen als Parameter übergeben könnte - uneingeschränkt, versteht sich!
Naja, theoretisch...
Praktisch sehe ich mir das morgen an, mal sehen!

Wie gut, dass ich nicht schon alle zwei Kopien*) der .pas-Datei überschrieben habe!

*) Habe immer zwei Kopien meiner Projektdateien: Eine in einem speziellen Ordner, eine auf USB-Stick.


jaenicke - Di 28.01.14 23:19

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Wie gut, dass ich nicht schon alle zwei Kopien*) der .pas-Datei überschrieben habe!
Kopien? Nicht dein Ernst, oder?
Hast du wirklich kein SVN-Repository oder ähnliches, in dem deine Quelltexte liegen? :shock:

// EDIT:
Ich nutze privat wie im Büro den VisualSVN Server:
http://www.visualsvn.com/server/
Kostenlos in der kleinen Version und absolut simpel zu benutzen.


Martok - Mi 29.01.14 03:23

Ich stör ja nur ungern eure lustigen Hacks, aber die LineInfo aus den Debuginfos auszulesen ist wirklich keine Option? Ich mein, ja, Delphi nutzt da ein abgedrehtes proprietäres Dateiformat ohne jeden Support von irgendwas, aber so schwierig ist das jetzt auch nicht (siehe user profile iconBenBE's Omorphia OmMap).

Nachtrag: Oh, und @Versionskontrolle: ja, Projekte mit mehr als 100 Zeilen ohne Versionskontrolle sind grob fahrlässig. Passiert mir auch ab und zu noch, aber es wird seltener...
Und zu SVN sag ich mal nix - es ist immer noch besser als gar kein System zu nutzen.


jaenicke - Mi 29.01.14 07:10

user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Und zu SVN sag ich mal nix - es ist immer noch besser als gar kein System zu nutzen.
SVN haben wir nur genommen, weil Git und Mercurial beide gar nicht oder nicht stabil als Dienst zum Laufen zu bekommen waren.
Ich sehe jetzt, dass es z.B. so etwas gibt:
http://bonobogitserver.com/
Hätte ich das seinerzeit gefunden (wenn es das da schon gab), hätten wir vielleicht auch das genommen.

So einfach wie VisualSVN (einfach nen Dienst) ist es dennoch nicht.

Die Debuginformationen auslesen dürfte mit einem (ggf. partiellen) Stacktrace am einfachsten sein, und das geht z.B. mit den JEDIs ja auch direkt, wie ich schon vorgeschlagen hatte.
Aber es macht das ganze natürlich auch nicht schneller. Da ist vermutlich sogar die Assertion schneller, oder?


Martok - Mi 29.01.14 15:53

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Aber es macht das ganze natürlich auch nicht schneller. Da ist vermutlich sogar die Assertion schneller, oder?
Ist halt die Frage wie man's macht ;)

Für BitSpace habe ich darauf basierend einen instrumentierenden Profiler entwickelt, der aus der Adresse (Get_pc_addr) beim Aufruf die Funktion/Zeile herleitet, das ist also durchaus nah-echtzeit machbar. Overhead ist im Schnitt bei <0.1ms/call.

Delphi-Quelltext
1:
2:
  {$DEFINE __PROFENTER:=uutlEmbeddedProfiler.ProfilerEnterProc(Get_pc_addr); try}
  {$DEFINE __PROFLEAVE:=finally uutlEmbeddedProfiler.ProfilerLeaveProc; end;}
(FPC hat tokenbasierte Macros)

Ist leider nicht ganz vergleichbar, da man in FPC über die Unit lineinfo direkt Zugriff auf die Symbole seiner Anwendung bekommt. Die lookup-Ergebnisse werden allerdings auch gecached, um jede Adresse nur einmal abfragen zu müssen. Ansonsten wäre das massiv IO-Bound auf der Symboldatei.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
    i:= fAddressCache.IndexOf(Events[WritePtr].Func);
    if i<0 then begin
      ce.Name:= '';
      ce.Line:= 0;
      ce.Src:= '';
      GetLineInfo(Events[WritePtr].Func,ce.Name,ce.Src,ce.Line);
      fAddressCache.Add(Events[WritePtr].Func, ce);
    end else
      ce:= fAddressCache.Data[i];


Womit wir allerdings wieder beim Problem vom Anfang wären. FPC verwendet in der Standardeinstellung Dwarf(2,3?)-Symbole, die sind einfacher zu lesen als Delphi oder TDB. Aber das könnte man ja aus den Jedis klauen, man muss ja nicht die komplette Lib nur dafür reinziehen... ;-)

Okay, ich hab ein paar Multithreading-Tricks und lustige Datenstrukturen verschwiegen die das so schnell machen, aber bisschen Geheimnisse haben wir ja auch :P


galagher - Mi 29.01.14 21:33

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Wie herrlich wäre doch die (Delphi)-Welt, wenn man an Prozeduren/Funktionen einfach Prozeduren/Funktionen als Parameter übergeben könnte - uneingeschränkt, versteht sich!
Naja, theoretisch...

Delphi-Quelltext
1:
LogCurrent(procedure begin Assert(False) end'Logstring');                    
Das geht ja durchaus...
Ich habe deinen Code eingebaut, an mein Projekt angepasst und es läuft! Phantastisch! Hätte das ohne deine Hilfe nie hinbekommen, vielen Dank! :zustimm:

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Wie gut, dass ich nicht schon alle zwei Kopien*) der .pas-Datei überschrieben habe!
Kopien? Nicht dein Ernst, oder?
Hast du wirklich kein SVN-Repository oder ähnliches, in dem deine Quelltexte liegen? :shock:
Nein, wenn du eine Versionskontrolle meinst. Ich habe nichts dergleichen. Ich kopiere einfach alle Dateien, das ist alles. Da ich ja bloss hobbymässig programmiere, reicht das doch!


jaenicke - Do 30.01.14 10:20

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Nein, wenn du eine Versionskontrolle meinst. Ich habe nichts dergleichen. Ich kopiere einfach alle Dateien, das ist alles. Da ich ja bloss hobbymässig programmiere, reicht das doch!
Hobbymäßig oder nicht, es ist trotzdem viel einfacher. Denn wenn du z.B. gerade etwas ausprobierst, brauchst du nur auf vergleichen gehen und siehst deine Änderungen usw., und du weißt auch was aktuell ist. Wenn du mehrere Kopien machst, kann es dir passieren, dass du an der falschen änderst oder ähnliches.

Das hat nichts damit zu tun, ob man das beruflich macht oder nicht. Ich mache das bei meinen privaten Projekten ganz genauso. Einfach weil es sehr viel einfacher ist. Und für Backups brauche ich nur immer das Repository sichern.


galagher - Do 30.01.14 21:04

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
kann es dir passieren, dass du an der falschen änderst oder ähnliches.
Man muss halt wissen, was man tut!
Wenn ich so etwas erst einmal gesehen habe, werde ich dir sicher zustimmen, aber da ich es ja nicht kenne... :nixweiss:


haentschman - Sa 01.02.14 10:09

Moin... 8)
Zitat:
aber da ich es ja nicht kenne...

...was der Bauer nicht kennt, das frißt er nicht. :P

Ich sage es mal einfacher... Ich hatte vor Jahren die gleiche Meinung wie du. Dann habe ich mich mich mal mit SVN beschäftigt. Heute kann ich mir nicht mehr vorstellen wie es ohne gegangen ist. Wenn man die Vorzüge, vor Allem die Übersicht über den Code, erkannt hat... ohne Versionskontrollsystem niemals. :P


jaenicke - So 02.02.14 00:48

Ich habe eben mal einen kleinen Überblick dazu geschrieben bzw. gebildert wie man ein solches SVN-Repository installiert und nutzt:
http://www.entwickler-ecke.de/viewtopic.php?p=683811
Die Bilder stammen von einer virtuellen Maschine mit Windows 7.