Autor Beitrag
Flamefire
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: Di 18.08.09 14:00 
Ich habe einen Traffic logger geschrieben, der alle Packete aus einer Anwendung mitschneiden soll (àla WPE Pro aber als Proxy)
Die Packete speichere ich in einem Array, um sie filtern zu können, und zeige sie in einem Listview an.

Das Problem: Wenn ich das Array beim 2. mal freigeben möchte bekomme ich "Ungültige Zeigeroperation" beim SetLength(...,0) oder eine Access Violation beim Freigeben der Einträge im Listview.

ausblenden volle Höhe 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:
  TEntry = record
    opcode:Word;
    data:TBytes;
    direction:Byte;
  end;
//...
    var Entry:Array[0..999of TEntry; //All entries are stored here
//...
procedure TFrameAnalyzer.Add(opcode: Word; direction: Byte; data: pointer;size:word);
var i:Integer;
begin
  if(curEntry=1000then begin//das soll die ersten 500 elemente freigeben und die letzten 500 darüber schieben

    for i := 0 to 500 - 1 do
      SetLength(Entry[i].data,0);//hier der fehler
    Move(Entry[500],Entry[0],SizeOf(TEntry)*500);
    for i := 500 to 1000 - 1 do
      Entry[i].data:=nil;
    curEntry:=500;
    UpdateListView;//oder hier die AV
  end;

//Write data
  Entry[curEntry].opcode := opcode;
  Setlength(Entry[curEntry].data,size);
  if(size>0then Move(data^,Entry[curEntry].data[0],size);
  Entry[curEntry].direction := direction;
  Inc(curEntry);
end;
//...
procedure TFrameAnalyzer.UpdateListView;
var i:Integer;
begin
  sListView1.Items.BeginUpdate;
  sListView1.Items.Clear;//hier die AV
  for i := 0 to curEntry - 1 do begin
    if(not ElemBlocked(Entry[i])) then//filter
      with sListView1.Items.Add do begin
        Caption:=IntToHex(Entry[i].opcode,4);
        SubItems.Add(Self.ByteToString(Entry[i]));
        SubItems.Add(StrDirection[Entry[i].direction]);
      end;
  end;
  sListView1.Items.EndUpdate;
end;


wenn ich "for i := 0 to 500 - 1 do SetLength(Entry[i].data,0);" entferne kommt kein Fehler mehr

edit:
ausblenden Delphi-Quelltext
1:
2:
3:
dass hier statt der schleifen funktioniert:
    for i := 0 to 500 - 1 do
      Entry[i]:=Entry[i+500];

aber warum?
ist ein dynamisches array in einem record nicht einfach ein pointer auf die array structur?
wird es dadurch nicht zum speicherleak? FastMM4 meldet zumindest nichts
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Di 18.08.09 14:18 
Bitte mit Finalize(Entry[i]) arbeiten, da das noch ein paar Dinge mehr macht als nur die Länge des Arrays auf 0 zu setzen. Sonst hast Du da u.U. ein Memleak drin.

Aber machst Du nicht einfach folgendes?

ausblenden 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:
procedure TFrameAnalyzer.Add(opcode: Word; direction: Byte; data: pointer;size:word);
var i:Integer;
begin
  if(curEntry=1000then 
  begin
    //das soll die ersten 500 elemente freigeben und die letzten 500 darüber schieben
    for i := 0 to 500 - 1 do
    begin
      Entry[i] := Entry[i+500];
      finalize(Entry[i+500]);
    end;

    curEntry:=500;
    UpdateListView;//oder hier die AV
  end;

//Write data
  Entry[curEntry].opcode := opcode;
  Setlength(Entry[curEntry].data, size);
  if size>0 then 
    Move(data^, Entry[curEntry].data[0],size);
  Entry[curEntry].direction := direction;
  Inc(curEntry);
end;

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.


Zuletzt bearbeitet von BenBE am Di 18.08.09 15:19, insgesamt 1-mal bearbeitet
Flamefire Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: Di 18.08.09 14:59 
ich glaube bei dir ist beim zitieren was schief gegangen ;-)

erstmal: Deine Lösung funktioniert ebenfalls, danke dafür

würde es nur gern verstehen:
1) was macht Finalize?
2) warum Finalize auf entry[i+500]?
wenn das array tatsächlich ein pointer auf die array structur ist, dann würde entry[x]:=entry[y] den inhalt des records kopieren. vermutlich macht der noch intern eine überprüfung bevor das array in x überschrieben wird (der hat ja einen referenzzähler drin. wenn der 0 ist gibt der das array frei, zumindest soweit ich das im debugger gesehen habe)
finalize auf entry[i+500] müsste mir doch dann das array, dess pointer ich nach entry[i] kopiert habe freigeben, wodurch es auf einmal fehlt, oder?
3) warum hat das mit dem Move nicht funktioniert?

PS: So weit ich das gesehen habe, wird finalize von setlength aufgerufen
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Di 18.08.09 15:29 
user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
ich glaube bei dir ist beim zitieren was schief gegangen ;-)

Nope, hatte beim Posten vergessen, überflüssige Teile deines Posts zu eliminieren ;-)

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
erstmal: Deine Lösung funktioniert ebenfalls, danke dafür

Als ob jemals Source von mir nicht funktioniert hätte :mrgreen:

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
würde es nur gern verstehen:
1) was macht Finalize?

F1, da werden Sie geHOLFEn ;-)

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
2) warum Finalize auf entry[i+500]?

Die Zuweisung in der Zeile zuvor erzeugt eine Kopie des Eintrags und inkrementiert somit den Referenzzählers im Array. Um diese Kopie des Arrays freizugeben - also Delphi bescheid zu geben, dass dieser Eintrag gelöscht werden soll - muss der Aufruf dort hin. Danach findet sich an dieser Stelle ein blanker, uninitialisierter Eintrag, den SetLength beim Beschreiben korrekterweise als leer erkennt und somit wieder korrekt Speicher zuordnen kann.

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
wenn das array tatsächlich ein pointer auf die array structur ist, dann würde entry[x]:=entry[y] den inhalt des records kopieren. vermutlich macht der noch intern eine überprüfung bevor das array in x überschrieben wird (der hat ja einen referenzzähler drin. wenn der 0 ist gibt der das array frei, zumindest soweit ich das im debugger gesehen habe)

Die Zuweisung macht eine ganze Menge Compiler Magic intern. Intern wird für [X] einmal Finalize aufgerufen, danach folgt ein Move, gefolgt von einem Update der Referenzzähler ALLER Member der Struktur, die dies benötigen.

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
finalize auf entry[i+500] müsste mir doch dann das array, dess pointer ich nach entry[i] kopiert habe freigeben, wodurch es auf einmal fehlt, oder?

Nope. Siehe Erklärung, was diese Zuweisung intern macht ...

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
3) warum hat das mit dem Move nicht funktioniert?

Weil Move die Referenzzähler nicht aktualisiert und du damit auf freigegebenen Speicher zugegriffen hast (daher die Exception mit der ungültigen Zeiger-Operation).

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
PS: So weit ich das gesehen habe, wird finalize von setlength aufgerufen

Korrekt. SetLength macht aber intern noch ne ganze Menge mehr. u.a. eine Initialisierung des zugewiesenen Speichers mit 0-Bytes.

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
Flamefire Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: Di 18.08.09 17:06 
gut soweit.
nochmal zu dem move:
ich brauche doch den referenzzähler nicht verändern

wenn ich das so mache(was ich zumindest vorhatte):

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
a,b:ptr;
New(a);New(b);
a:=iwas;
b:=iwas;
Free(a);
a:=b;
b:=nil;

dann sollte in der theorie doch alles i.o. sein, oder?
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Di 18.08.09 17:59 
user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
gut soweit.
nochmal zu dem move:
ich brauche doch den referenzzähler nicht verändern

wenn ich das so mache(was ich zumindest vorhatte):

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
a,b:ptr;
New(a);New(b);
a:=iwas;
b:=iwas;
Free(a);
a:=b;
b:=nil;

dann sollte in der theorie doch alles i.o. sein, oder?

Du vergisst, dass der Compiler auch noch ein Wörtchen mitzureden hat.Bei deinem ursprünglichen Code sind a[0] und a[500] identisch, d.h. du hast 2! Kopien deines speichers, der Referenzzähler des Arrays ist aber 1. Wenn Du dann mit SetLength in einem der Arrays den Speicher freigibst, was wird dann wohl mit dem Zeiger auf des anderen Eintrags??? Korrekt: Der wird ungültig.

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
Flamefire Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: Di 18.08.09 18:18 
ich habe aber doch kein a[500] mehr nach:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
    for i := 0 to 500 - 1 do
      SetLength(Entry[i].data,0);//hier der fehler
    Move(Entry[500],Entry[0],SizeOf(TEntry)*500);
    for i := 500 to 1000 - 1 do
      Entry[i].data:=nil;

danach habe ich ein ordentliches array in a[0] und ein nil statt eines arrays in 500
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Di 18.08.09 18:44 
In a[0] steht dann aber ein finalisiertes Array, weil der Compiler die Referenz freigegeben hat. nil zuweisen bei einem Array führt implizit ein Finalize aus ...

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
Flamefire Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: Di 18.08.09 18:50 
geht klar danke