Code: Alles auswählen
RETURN VALUE The alloca() function returns a pointer to the beginning of the allocated space. If the allocation causes stack overflow, program behaviour is undefined.
Code: Alles auswählen
RETURN VALUE The alloca() function returns a pointer to the beginning of the allocated space. If the allocation causes stack overflow, program behaviour is undefined.
Direkt mit writeln sieht man es in Echtzeit und muss nicht noch eine Datei angucken. Wie ich schon gesagt habe, wen man man schnell einen wert angucken will. Für Debugln() muss man schon wieder eine Unit einbinden. Auch kann man mit Writeln in der Schnelle einen Integer oder Boolean ausgeben, ohne grosse Typenumwandlung.Wenn du die Unit LazLogger verwendest und von WriteLn() auf DebugLn() umstellst, musst du deinen Code nicht ändern. Du kannst das ganze sogar produktiv verwendet und deine Anwender erstellen im Hintergrund eine Logdatei, die du einfach auswerten kannst.
Code: Alles auswählen
uses
{$ifndef Release}LazLogger,{$endif}
....
{$ifndef Release} DebugLnEnter({$I %FILE%} + '->' +{$I %CURRENTROUTINE%}); {$endif}
Code: Alles auswählen
..
{$R *.res}
{$if declared(UseHeapTrace)}
const
co_heaptrc = 'heaptrace.txt';
{$endif}
begin
// If you want to show heaptrc report dialog only if there were leaks
// in your application, then put this command somewhere
// in your main project source file:
{$if declared(UseHeapTrace)}
GlobalSkipIfNoLeaks := true; // supported as of debugger version 3.1.1
if FileExists(co_heaptrc) then
DeleteFile(co_heaptrc);
SetHeapTraceOutput(co_heaptrc); // supported as of debugger version 3.1.1
// HaltOnError := false; // dont halt a the end of the programm
{$endif}
DebugLnEnter('****************************************************************');
DebugLn('Starting...');
RequireDerivedFormResource:=True;
Application.Initialize;
Application.CreateForm(TFormXXX, FormXXX);
Application.Run;
DebugLnExit('...Finishing');
end.
Darum hab ich mir meine eigne Unit geschrieben (siehe ein paar Posts vorher) der beim Debüt Build auf stdout schreibt und beim Release Build eine Datei schreibt. Ich muss aber auch ehrlich sagen, ich hab die Unit bereits schon genutzt bevor ich von lazlogger überhaupt wussteMathias hat geschrieben:Direkt mit writeln sieht man es in Echtzeit und muss nicht noch eine Datei angucken. Wie ich schon gesagt habe, wen man man schnell einen wert angucken will. Für Debugln() muss man schon wieder eine Unit einbinden. Auch kann man mit Writeln in der Schnelle einen Integer oder Boolean ausgeben, ohne grosse Typenumwandlung.Wenn du die Unit LazLogger verwendest und von WriteLn() auf DebugLn() umstellst, musst du deinen Code nicht ändern. Du kannst das ganze sogar produktiv verwendet und deine Anwender erstellen im Hintergrund eine Logdatei, die du einfach auswerten kannst.
Hast du mal die Deklaration von Debugln angeguckt, das sieht aus wie zu Turbo-Pascal Zeit, als es noch keine offene Arrays gab.![]()
Da war Write(ln) schon sehr fortschrittlich. Ein Befehl, der (fast) alles frisst.
Nein, bin mir da gar nicht sicher. Ich kenn die Methode (regelmaessig den Stack-Zeiger abzufragen und eine initialisierte "Schutzvariable" am Ende des vorgesehenen Stackbereichs abzufragen ob sie ihren initialisierten Wert verloren hat) von der Mikrocontrollerprogrammierung. Konnte mir hier nichts anderes drunter vorstellen. Die von dir zitierte Fehlermeldung spricht auch nicht dafuer, dass der Stack vom Betriebssystem getrennt ueberwacht wird oder in einem getrennten Speicherbereich liegen wuerde. Aber ich sollt vielleicht nichts mehr dazu sagen, sind schon zu viele Spekulationen~Warf hat geschrieben: Bist du dir da sicher? denn die Stack Limits werden afaik vom Betriebsystem enforced (z.b. unter linux in der limits Datei), zwar muss das OS kein limit haben (z.b. linux: ulimit -s unlimited), aber normalerweise ist ein Limit eingestellt.
Weil Anfänger dies fragen, willst Du, dass Anfänger ihre Fehler (was Anfängern nun mal meist passiert) durchgehen? Das kann schnell zu viel mehr Fragen führen, und zwar jedes mal, wenn das Programm nicht stabil läuft, und zugleich auch von Lazarus dazu keine Fehlermeldung gibt. Und das wird dann besonders 'lustig' dann heraus zu finden, wo der Fehler liegt, während eine besser Einstellung der Debugger einem dies längst mitgeteilt hätte. Hinzu komme, je später man lernt, dass es so nicht gemacht werden darf, desto schwerer wird es, sich das richtige dann anzugewöhnen. Gerade für Neulinge sollte es daher jeden Fehler aufzeigen.Mathias hat geschrieben:Ich finde es gut, das es per Default deaktiviert ist. Wegen mir könnte man auch den Debugger deaktivieren. Da kommen sowieso im nur Fragen bei Anfängern, wieso die EXE so aufgebläht ist.
Wen jemand fortgeschritten ist, und weis wie das Debugging funktioniert, dann weis er auch wo man es einschaltet.
Ich bekomme mit 15 in meinen Beispiel ja auch keine SIGSEV, sondern beim beenden des Programms eine verwirrende Fehlermeldung.Warf hat geschrieben:Zufällig ist es nicht ganz, er befindet sich ganz oben auf dem Stack (welcher nach oben wächst) was also bedeutet, wenn er nach oben über den array schreibt müsste er schon über alle Stackframes drüber schreiben bis es zu einer Zugriffsverletzung kommt, und der stack ist in produktiven programmen für gewöhnlich recht groß. Daher ist es recht unwahrscheinlich bei Stack-Arrays eine Zugriffsverletzung zu bekommen.Mathias hat geschrieben:Zufällig war der Speicherbereich frei, ansonsten hättest eine SIGSEV gehabt.
Bin der Ansicht, der Debugger sollte so viele Fehler wie möglich finden. Erst recht bei Anfänger, damit die gleich das richtige Wissen, was eben geht, und was nicht, vermittelt bekommen. Anstatt Programme zu schreiben, die einem später dann unerwartet um die Ohren fliegen.Warf hat geschrieben: Nein standardmäßig ist praktisch die minimale funktionalität gegeben um den GDB zu verwenden. Für kleinere sachen ausreichend, für größere Projekte würde ich einfach die Default Build-Modes Debug und Release verwenden.
Danke. Kann mich nicht erinnern, dass der wichtige Teil wo im Buch beschrieben war. Allerdings ist das wichtige ja eher versteckt, falls überhaupt darüber geschrieben wird, und belangloses am Anfang.Warf hat geschrieben: Lazarus kann dir den Standard Release und Debug Modus per klick erstellen, die haben ziemlich gute voreinstellungen: Projekteinstellungen->Compilereinstellungen, oben Erstellmodi haken setzten, auf den ... button clicken und dann auf Debug Und Release Modus erstellen klicken. Den Default modus kannst du dann löschen.
Über die Dropdown box kannst du dann den aktiven modus wählen
Wie ich bereits selber sagte: Blicke da eh nicht so ganz durch. Aber genau das ist immer das Gefährliche, wenn Jemand etwas nicht selber erklärt (in dem Fall Lazarus/-Buch etc.), sondern es anderen überlässt. Irgendwann müssen die dann doch erklären, was die Wahrheit ist und haben viel Aufräumarbeit (Beseitigung von Falschinfos und Überzeugung) dann vor sich.Warf hat geschrieben: Wie gesagt in den meißten fällen wird dir das Betriebsystem nicht genug speicher zur Verfügung stellen damit du überhaupt eine so genannte heap collision (stack der nach oben wächst und heap der nach unten wächst überschneiden sich) bekommen kannst. Man muss schon aktiv, mit administratorrechten das bei seinem Betriebsystem einstellen damit man da was kaputt machen kann.
Moment mal, soll das heißen, dass allein die Größe für einen Überlauf sorgt? Wenn ich jetzt zum Beispiel folgendes machen würde:Warf hat geschrieben: Das hauptproblem für eine Stacküberlauf sind aber auch keine angreifer, sondern dumme programmierer. z.B funktionen mit großem stack:Ein entsprechend großes i würde dir damit eine heap collisiob geben. Aber, mit dynamischen arrays oder pointer auf statische arrays und new-dispose, wäre das ganze kein problemCode: Alles auswählen
procedure SomeRecursiveProcWithVeryLargeStack(i: Cardinal); var arr: array[0..1000000] of Byte; // 1MB stack size begin if LongBool(i) then SomeRecursiveProcWithVeryLargeStack(i-1); end;
Code: Alles auswählen
var arrd3: array[1..1000000,1..20] of Integer; // also mind. 20 MB, würde das dann auch einen Überlauf verursachen?
Sehe ich genau so. Und falls es die bereits gibt, so scheint die dann gut versteckt zu sein.Warf hat geschrieben: Es bräuchte einfach eine vernünftige Wikiseite die alles wenigstens kurz erklärt (und vor allem alles auf einer Seite)
Ja, unter meinem OpenSuse Linux ist die standard größe für den Stack (ulitmit -s) eta 8MB. Das heißt(ich habs auch grad ausgeführt):Erwin hat geschrieben:Moment mal, soll das heißen, dass allein die Größe für einen Überlauf sorgt? Wenn ich jetzt zum Beispiel folgendes machen würde:Code: Alles auswählen
var arrd3: array[1..1000000,1..20] of Integer; // also mind. 20 MB, würde das dann auch einen Überlauf verursachen?
Code: Alles auswählen
procedure foo();
var arrd3: array[1..1000000,1..20] of Integer;
begin
arrd3[1,1] := 0;
end;
begin
Foo;
end.
Code: Alles auswählen
Min Addr
#----------#
# CODE #
# (RX) #
#----------#
#----------#
# Const #
# (R) #
#----------#
#----------#
# Data #
# (RW) #
#----------#
#----------#
# Heap #
# (RW) #
#vvvvvvvvvv#
............
#^^^^^^^^^^#
# Stack #
# (RW) #
#----------#
Max Addr
Code: Alles auswählen
| FormButtonClick |
|-----------------|
|ButtonHandleClick|
|-----------------|
(.................)
|-----------------|
| Main |
Code: Alles auswählen
procedure InfiniteLoop;
var i: Integer;
arr: array[1..1] of Integer;
begin
for i:=0 to Length(array)-1 do
arr[i] := -1; // das überschreibt i da i vor arr liegt
end;
Damit hat er kein Problem, aber mit 15: TestArray[15]:=25;Warf hat geschrieben: mit dem TestArray[5]:=25; schreibst du 8 Byte über deine Framgerenze mindestens ins ButtonHandleClick oder so rein.
Ist die Grenze nicht bei 16 Exbibyte ?Ich habe aber ein 64-Bit Computer und BS, wenn mich nicht alles täuscht. Also BS ist 64-Bit. So weit ich weiß, muss es dann der Computer auch 64-Bit sein. Sollte da dann also die Grenze doch bei 128 GB liegen?
Die Theoretische grenze ja, aber man darf nicht vergessen, je größer der Addressraum desto aufwendiger ist es diesen zu organisieren (die allokationstabellen werden viel größer und komplizierter), daher implementieren Windows, und OSX nur deutlich kleinere Addressräume, das gibt schlicht weg eine bessere performance und weniger overhead. Windows 10 Home kann 128G und Pro 512G. OSX befindet sich in ähnlichen größenordnungen. Linux kennt glaube ich keine beschränkung und kann die vollen 16 EB ausnutzen, ist aber wahrscheinlich bei sehr großen ramzahlen dann eventuell sogar deutlich langsamer.Mathias hat geschrieben:Ist die Grenze nicht bei 16 Exbibyte ?Ich habe aber ein 64-Bit Computer und BS, wenn mich nicht alles täuscht. Also BS ist 64-Bit. So weit ich weiß, muss es dann der Computer auch 64-Bit sein. Sollte da dann also die Grenze doch bei 128 GB liegen?
Vorausgesetzt, es sind genügend RAM-Riegel verbaut.
das ist der Stackframe der main-funktion, also die erste Funktion die beim start des programmes ausgeführt wird (im pascal ist das der begin-end block der lpr datei)Erwin hat geschrieben:Danke erst mal, für all diese Erklärungen. Da weiß man zumindest schon mal wieder vieles mehr.
Main frame? Von dem ich nichts wissen soll?
Ja muss auch sagen 5 hätte mich etwas gewundert. 12*4 sind 48 Byte, damit reist du den stack ganz schon tief runter und landest entweder in der LCL internen update funktion oder in der main funktionErwin hat geschrieben:Damit hat er kein Problem, aber mit 15: TestArray[15]:=25;
Aber läuft der Erklärung betreffend vermutlich auf das selbe hinaus. Nur halt Ordnungshaber, falls jemand anders dies ebenfalls testet.
Ne, 100x 1MB wird genauso die stack größe belegen, durch alignment eventuell sogar mehr. Und der Data, Code und Const bereich steht zur Compilezeit fest. Das sind alle globalen variablen, konstanten und natürlich dein sourcecode selbst. Nur heap und Stack sind dynamisch. Aber es kann natürlich sein das das betriebsystem irgendwas unanständiges macht, was ich nicht kenne (so gut kenne ich mich da auch nicht aus)Erwin hat geschrieben:Aber dennoch sind bei mir Array-Größen teils ganz schön beschränkt.
Irgendwie verwirrend.
Kann es sein, dass einerseits ein großes 8MB Array nicht geht, aber 100 x 1 MB Array schon?
Steht der Speicherbereich der Segmente für Code, Const und Data Anfangs fest, oder wird nach dem Start je nach Bedarf dann erst mal Reserviert und steht erst danach dann fest?
Der heap und der stack können so viel platz belegen wie noch frei ist. Auf einem 32 bit windows, da Code und Data oft vernachlässigbar sind(<100mb), hätte man immernoch fast 3 GB für heap und Stack frei. Die einzige begrenzung sind die stacklimits wie ich bereits gesagt habe.Erwin hat geschrieben:Und den Rest teilt sich generell dann je nach Bedarf Heap und Stack? Oder steht deren gemeinsame Größe ebenfalls generell fest?
Kann es überhaupt den ganzen Speicherbereich nutzten, oder wird auch da ihnen nur ein Teil zugewiesen?
Das alles habe ich irgendwie ein wenig ... sparsamer erklärt bekommen. Deshalb ist es für mich sehr ... verwirrend ... überraschend. Derweil scheint mir dies, zumindest ab einer bestimmten Programmgröße, sehr wichtig zu sein, zu wissen.
Ich habe aber ein 64-Bit Computer und BS, wenn mich nicht alles täuscht. Also BS ist 64-Bit. So weit ich weiß, muss es dann der Computer auch 64-Bit sein. Sollte da dann also die Grenze doch bei 128 GB liegen?
Heißt dass, man kann die Größe in den Compiler-Einstellungen einstellen?Warf hat geschrieben: Ne, 100x 1MB wird genauso die stack größe belegen, durch alignment eventuell sogar mehr. Und der Data, Code und Const bereich steht zur Compilezeit fest. Das sind alle globalen variablen, konstanten und natürlich dein sourcecode selbst. Nur heap und Stack sind dynamisch. Aber es kann natürlich sein das das betriebsystem irgendwas unanständiges macht, was ich nicht kenne (so gut kenne ich mich da auch nicht aus)
Ups da hab ich mich falsch ausgedrückt, es belegt nicht gleich viel speicher, sondern nicht weniger, also das problem von zu großen objekten kann man nicht mit vielen kleinen objekten beheben. Das einzige was einstellbar ist, ist das alignment, es wird normalerweise immer auf 4 Byte aufgerundet, damit es besser optimiert werden kann.Erwin hat geschrieben:Heißt dass, man kann die Größe in den Compiler-Einstellungen einstellen?
Und hängt der feste Bereich dennoch von den Arbeitsspeicher ab, oder ist er überall dann gleich?
Taskmanager öffnen und auf den Ramverbrauch achten (oder unter linux htop)Erwin hat geschrieben:Habe jetzt mal mit Array getestet. Bei mir geht das bis 530 Millionen (also [0..530000000]). Single und Integer. String halb so viel.
Wenn ich recht bedenke, dazu kommt doch bei 64-bit-System noch mal mindestens 4 mal so viel Speicherplatz .... also ca. 2-3 GB dann insgesamt, was es dann beansprucht? Kann das sein?
Eigentlich groß genug. Will ja gar nicht so viel Platz mit meinen Programmen beanspruchen.
Musst du dir halt generieren lassenErwin hat geschrieben:Ein Test, ob man mit Const genau so viel beanspruchen kann, ging leider schief, da man ja die Array sofort 'füllen' müsste, und ich nicht wüsste, wie das geht, ohne sich die Finger wund zu schreiben.
[/quote]Erwin hat geschrieben:Die genauen Zahlen würden mich dennoch interessieren. Wie groß ein Programm selbst (also CODE-Teil) dann sein dar/istf? etc. Aber vermute mal, dass kann man nicht mal so ohne weiteres festlegen/feststellen?
Also der Tipp hört sich für mich wieder so an, als würden doch alle 5 sich den Speicherbereich je nach Bedarf teilen. Weil man sieht ja meist nur insgesamt, was an Speicher von einem Programm benötigt wird, aber nicht ob der Platz für CODE, Variablen, etc. zu eng wird?Warf hat geschrieben: Taskmanager öffnen und auf den Ramverbrauch achten (oder unter linux htop)
Ansonsten, wenn du windows verwendest, ich kenn das stacklimit nicht von windows, ich hab unter Linux getestet, da ich da ulimit verwenden kann um das limit zu setzen und rauszufinden
Um den Code mach dir gar keine gedanken, darum kümmert sich der Compiler. Solang der nicht meckert ist alles in ordnung. Und das ganze ist nicht von Computer zu Computer sondern von Architektur zu Architektur und zwischen Optimierungsstufen anders sein. z.B. unter AVR architekturen gibt es normalerweise einen ROM (Flash) auf dem Code und Consts liegt, und einen RAM in dem dann dein stack und die globalen variablen liegen. Den heap darfst du selbst implementieren. Gleichzeitig sind AVR's RISC's (Reduced Instruction set Computer) und x86 sind CISC's (complete ...), daher haben x86 viel mehr instructions, und damit kannst du den selben code in weniger instructions speichern.Erwin hat geschrieben:Also der Tipp hört sich für mich wieder so an, als würden doch alle 5 sich den Speicherbereich je nach Bedarf teilen. Weil man sieht ja meist nur insgesamt, was an Speicher von einem Programm benötigt wird, aber nicht ob der Platz für CODE, Variablen, etc. zu eng wird?
Außerdem gewinne ich den Eindruck, dass es also generell von Computer zu Computer unterschiedlich ist, ungeachtet dessen, ob dies sein müsste?
Während meiner also noch einen Adressraum von ... 530 Millionen (Single oder Integer) Array-Größe hat, kann es auf einem anderen Computer zu groß sein? Auch dann, wenn dieser gleich viel Arbeitsspeicher hat?
Vermutlich mache ich mir wohl eh zu viele Gedanken. Also was den CODE betrifft, da werde ich wohl eh Jahre proggen müssen, um dessen Rahmen zu sprengen, vermute ich mal?
Und auch sonst ... 3-4 GB Arbeitsspeicher sollte ein Programm eigentlich nicht brauchen, oder?
Ach, was variablen betrifft, bis ich da was schreibe, wo dies wirklich so groß werden könnte, ist vielleicht eh einiges anders. Auch sonst sollte ich erst warten, bis das Problem akut wird.
Jedenfalls scheint es wohl dennoch so viel Platz zu geben, dass ich mir darüber nicht wirklich den Kopf zerbrechen muss, oder?
Ein klares nein. Nur weil ein Compiler nicht meckert ist der Code nicht in Ordnung. Wenn der Compiler nicht meckert heisst das nur das die Syntax stimmt - mehr nicht. Ob der Code korrekt ist, kann er nicht feststellen. Vor allen wenn man in Richtung Pointer oder Casts geht, kann der Compiler nicht alles prüfen.Warf hat geschrieben:Um den Code mach dir gar keine gedanken, darum kümmert sich der Compiler. Solang der nicht meckert ist alles in ordnung.