[WinAPI] Größe des Stapelspeichers bestimmen

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

[WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von Krishty »

Der Titel ist Programm – ich brauche die Größe des Stapelspeichers in meinem laufenden Prozess.

Bietet die WinAPI nativ etwas dafür an?

Mein Plan A ist, über __ImageBase an den PE-Header meines Prozesses zu kommen (irgendwie); dort müsste ich in der IMAGE_OPTIONAL_HEADER-Struktur das SizeOfStackReserve-Attribut auslesen können. Das große Problem an dem Plan ist, dass er nicht für Threads funktioniert, denen man via CreateThread() eine andere Stapelgröße vorgegeben hat …

Das endgültige Ziel ist es übrigens, den im Stapelspeicher verbleibenden Freiraum zu bestimmen. Falls ich die Größe des Stapelspeichers hätte, könnte ich durch die aktuelle Adresse und durch die Basisadresse bestimmen, wie viel Freiraum noch übrig ist. (Das dürfte aber leider auch /DYNAMICBASE zunichte machen, weil dann eine globale Variable die Basisadresse enthielte.)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von dot »

Was genau willst du mit diesem Wissen dann anstellen!?
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von eXile »

Ist hier etwas für dich dabei? (Man beachte bitte auch den letzten Satz von der dortigen ersten Antwort.)
joggel

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von joggel »

Man könnte doch mit _malloc soviel Speicher reservieren bis die Exception geworfen wird. dann hat man es.
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von Krishty »

dot hat geschrieben:Was genau willst du mit diesem Wissen dann anstellen!?
Ich möchte halbwegs intelligent entscheiden, ob ich Daten fast kostenlos auf dem Stapelspeicher verarbeiten kann oder den Umweg über eine Freispeicherallokation gehen muss. Oder – noch lieber – bestimmte Dinge nur noch auf dem Stapel verarbeiten und im Falle eines Überlaufs elegant scheitern statt mit einer nichtbehandelten Ausnahme abzustürzen.
eXile hat geschrieben:Ist hier etwas für dich dabei? (Man beachte bitte auch den letzten Satz von der dortigen ersten Antwort.)
Ja, dankeschön! Stack Overflow ist nicht wirklich prädestiniert dafür, dort nach „Stack Overflow“ zu suchen; „Stack Space“ kam mir leider nicht in den Sinn :)
joggel hat geschrieben:Man könnte doch mit _malloc soviel Speicher reservieren bis die Exception geworfen wird. dann hat man es.
Ich hatte jetzt eher nach was mit konstanter Laufzeit gesucht :)

Ich könnte auch einfach einen Übersetzer von Structured zu C++-Exceptions setzen und den Stack-Overflow so in was bad_alloc-mäßiges verwandeln; allerdings darf _resetstkoflw() nicht von catch-Blöcken aus aufgerufen werden. Das macht die Sache unheimlich kompliziert.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von dot »

Krishty hat geschrieben:
dot hat geschrieben:Was genau willst du mit diesem Wissen dann anstellen!?
Ich möchte halbwegs intelligent entscheiden, ob ich Daten fast kostenlos auf dem Stapelspeicher verarbeiten kann oder den Umweg über eine Freispeicherallokation gehen muss. Oder – noch lieber – bestimmte Dinge nur noch auf dem Stapel verarbeiten und im Falle eines Überlaufs elegant scheitern statt mit einer nichtbehandelten Ausnahme abzustürzen.
Naja gut, VirtualQuery() wurde eh schon in eXiles Link erwähnt. Die Frage ist eben, ob die ganzen Syscalls zum Rausfinden ob noch Platz ist, am Ende nicht langsamer sind, als es einfach gleich am Heap anzulegen...
Krishty hat geschrieben:Stack Overflow ist nicht wirklich prädestiniert dafür, dort nach „Stack Overflow“ zu suchen
Also ich musste das rein aus Prinzip schon mindestens einmal machen :P
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von Krishty »

dot hat geschrieben:Die Frage ist eben, ob die ganzen Syscalls zum Rausfinden ob noch Platz ist, am Ende nicht langsamer sind, als es einfach gleich am Heap anzulegen...
Sind sie sogar ganz bestimmt :( Ich überlege, ob ich das nicht bloß einmal beim Starten abfragen und dann die Basisadresse und Größe speichern soll. Die Probleme sind nur a), dass das beim Start jedes neuen Threads geschehen und via TLS verwaltet werden müsste; und b), dass ich mir damit /DYNAMICBASE vermiese (wobei ich immernoch nicht weiß, wie ich überhaupt dazu stehen soll).
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von dot »

Naja, der Hack über den ThreadInfoBlock dürfe ziemlich performant sein, weil keine Syscalls. Ist aber eben leider nur ein Hack. Ich frag mich halt immer noch, inwiefern das, was du da vorhast, wirklich sinnvoll ist. Warum genau musst du zur Laufzeit entscheiden, ob du was am Stack oder am Heap anlegst? Kommt mir jedenfall irgendwie seltsam vor...
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von Krishty »

Weil der Stapel nicht beliebig wachsen kann, also auch nicht beliebig große Datenmengen darauf verarbeitet werden können. Das Problem ist, dass sich der volle Stapel – im Gegensatz zum Freispeicher – enorm schlecht elegant behandeln lässt. catch(::std::bad_alloc const &) ist da nicht; sondern man hat sofort einen ungültigen Programmzustand und muss den entweder über einen Structured Exception Handler wieder zurechtbiegen oder den ganzen Prozess mit einem Knall abstürzen lassen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von dot »

Mir ist das Problem schon klar. Ich frag mich nur, was genau für einen seltsamen Anwendungsfall du da hast, dass du das nicht zur Compiletime entscheiden kannst.
Benutzeravatar
Lynxeye
Establishment
Beiträge: 145
Registriert: 27.02.2009, 16:50
Echter Name: Lucas
Wohnort: Hildesheim
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von Lynxeye »

Die Frage die sich bei alledem stellt ist doch, was versprichst du dir davon die Verarbeitung auf dem Stack, statt auf dem Heap, auszuführen?
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von Krishty »

Die Allokation zu sparen :) Es geht vor allem um das Anhängen von Nullen an Strings, da die in meinen Programmen kaum noch abschließende Nullen haben (stattdessen eine Längenangabe) und z.B. die WinAPI nichts anderes als NullEnden erwartet. Das hätte ich schon gern irgendwie eleganter. C99s Variable Length Arrays sind echt was, was mir in C++ fehlt :(
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Lynxeye
Establishment
Beiträge: 145
Registriert: 27.02.2009, 16:50
Echter Name: Lucas
Wohnort: Hildesheim
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von Lynxeye »

Hast du schon mal über alloca() nachgedacht? Ist zwar nicht ganz so elegant wie eine reine Stackoperation, gibt dir aber eine ordentliche Exception, wenn kein Speicher auf dem Stack vorhanden ist, in dem Falle kannst du dann auf den Heap zurückfallen oder was auch immer dir in dem Falle beliebt.
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von Krishty »

Darum geht es ja gerade :) alloca() wirft eine strukturierte Ausnahme, was bedeutet:
  • Die Funktion kann nicht mehr geinlined werden
  • Die Funktion darf keine try-catch-Blöcke oder throws beinhalten
  • Man muss die Stapelintegrität manuell wiederherstellen, falls eine Ausnahme geworfen wurde
Also z.B.:

bool hasStackOverrun = false;
__try {
    void * data = alloca(dataSize);
    // mach was mit dem Speicher
} __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) {
    _resetstkoflw();
    hasStackOverrun = true;
}
if(hasStackOverrun) {
    // via Rückgabewert mitteilen, damit die übergeordnete Funktion eine Ausnahme wirft (wir können das hier nicht, da diese Funktion SEH nutzt)
}


eleganter fände ich:

if(dataSize > availableStackSpace) {
    throw ::std::bad_alloc();
}
void * data = alloca(dataSize); // alloca() *dürfte* nicht mehr scheitern können; falls doch, ist terminate() O.K. (höhere Macht)
// mach was mit dem Speicher


Es gibt noch die Möglichkeit, den Structured Exception Handler durch was Eigenes zu überschreiben, was eine C++-Ausnahme auslöst. Allerdings könnte ich dann nicht mehr automatisch _resetstkoflw(); aufrufen, sondern müsste das im nächsten catch-Block tun. Das wäre, gelinde gesagt, suboptimal.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von dot »

Kannst du deine Strings nicht einfach um 1 Zeichen länger machen und eine 0 reinpacken, dann brauchst du nicht mehr kopieren/allokieren als du sowieso musst!? Und ansonsten muss man sich ja nicht auf den Stack verbeißen. Man könnte sich auch direkt einen eigenen Stringpool oder sowas basteln, der genau das tut, was man will.
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von Krishty »

dot hat geschrieben:Kannst du deine Strings nicht einfach um 1 Zeichen länger machen und eine 0 reinpacken, dann brauchst du nicht mehr kopieren/allokieren als du sowieso musst!?
Könnte ich, will ich aber nicht. Ist mir zu blöd. Genau, wie Zeilenvorschübe vor meine Zeilenumbrüche zu packen :) Außerdem gäbe das dann Probleme mit Kompression und so.
dot hat geschrieben:Und ansonsten muss man sich ja nicht auf den Stack verbeißen. Man könnte sich auch direkt einen eigenen Stringpool oder sowas basteln, der genau das tut, was man will.
Über einen gesonderten Kritzelspeicher habe ich auch schon nachgedacht; leider müsste der aber wieder Thread-lokal ausfallen … ist doch scheiße. Das wäre der Stapel nämlich automatisch; der könnte auch nicht lecken und zu guter Letzt ist er ja sowieso immer da; auch, wenn man ihn nicht beachtet … wie eine Katze.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von dot »

Krishty hat geschrieben:
dot hat geschrieben:Kannst du deine Strings nicht einfach um 1 Zeichen länger machen und eine 0 reinpacken, dann brauchst du nicht mehr kopieren/allokieren als du sowieso musst!?
Könnte ich, will ich aber nicht. Ist mir zu blöd. Genau, wie Zeilenvorschübe vor meine Zeilenumbrüche zu packen :) Außerdem gäbe das dann Probleme mit Kompression und so.
Seh nicht ganz, wo das Problem ist. Du arbeitest ja offenbar nur mit den Längen und die 0 ist in der Länge sowieso nicht enthalten!? Wäre doch sicher nur eine kleine Modifikation deiner Stringklasse, die eigentlich keine Auswirkungen auf irgendwas sonst haben sollte. Und du bekommst dafür Kompatibilität mit C-Strings in O(1) mit minimalem c ;) Du könntest deine Strings überhaupt so mit 0en padden, dass sie immer schön aligned sind, was das Kopieren von Strings und einige andere Operationen evtl. sogar schneller macht, da durchgehend mit SSE gefahren werden kann und nie einzelne Bytes angegriffen werden müssen.
Krishty hat geschrieben:
dot hat geschrieben:Und ansonsten muss man sich ja nicht auf den Stack verbeißen. Man könnte sich auch direkt einen eigenen Stringpool oder sowas basteln, der genau das tut, was man will.
Über einen gesonderten Kritzelspeicher habe ich auch schon nachgedacht; leider müsste der aber wieder Thread-lokal ausfallen … ist doch scheiße. Das wäre der Stapel nämlich automatisch; der könnte auch nicht lecken und zu guter Letzt ist er ja sowieso immer da; auch, wenn man ihn nicht beachtet … wie eine Katze.
Stimmt, der wäre idealerweise threadlokal. Aber du wüsstest immer, wieviel Platz drauf ist. Außerdem: Bist du dir sicher, dass die Allokation überhaupt das Problem ist? Ich mein, du brauchst den Speicher ja, um ständig strings rumzukopieren, was auch nicht ganz gratis ist. Rein prinzipiell würde ich mal meinen, du optimierst da an der falschen Stelle. Entweder du brauchst die C-Kompatibilität nur in manchen Randbereichen, was bedeuten würde, dass die Performance wohl sowieso keine Rolle spielt, oder du brauchst die C-Kompatibilität ständig bzw. an sehr sensiblen Stellen. In letzterem Fall wäre es wohl sowieso besser, ganz ohne Allokationen bzw. das Kopieren auszukommen. Wenn deine Strings einfach schon von Haus aus ne 0 dran hätten, dann sparst du dir nicht nur beliebig komplexe Allokationen, sondern auch das ganze rumkopieren. Ich würd mal meinen, die 0 einfach mit reinzupacken, hat praktisch nur Vorteile...
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von Krishty »

dot hat geschrieben:Seh nicht ganz wo das Problem ist. Du arbeitest ja offenbar nur mit den Längen und die 0 ist in der Länge sowieso nicht enthalten!? Wäre doch sicher nur eine kleine Modifikation deiner Stringklasse, die eigentlich keine Auswirkungen auf irgendwas sonst haben sollte. Und du bekommst dafür Kompatibilität mit C-Strings in O(1).
Das Problem ist, dass es Idiotie ist. Ich habe keinen Bock, alle meine Methoden zu überarbeiten damit sie auch bloß diese verdammte Null am Ende einfügen, und das dann zu vergessen und von Zugriffsverletzungen dran erinnert zu werden. Bullshit Bullshit Bullshit. Ich will ein kohärentes, minimales Konzept verfolgen und mir das nicht davon kaputtmachen lassen, dass die WinAPI 1982 von einem Haufen betrunkener Zigeuner zur Steuerung eines Kirmeskarussels entwickelt wurde.
dot hat geschrieben:Außerdem: Bist du dir sicher, dass die Allokation überhaupt das Problem ist?
Nö. Wenn ich Probleme hätte, müsste ich mir nicht über die Reinheit des großen Ganzen den Kopf zerbrechen :) Keine Probleme, keine Engpässe. Mir passt es nur nicht, dass etlicher Kleinkram einen WinAPI-Aufruf kostet, wo ich auch einfach von Stapel leben könnte.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von dot »

Krishty hat geschrieben:Ich habe keinen Bock, alle meine Methoden zu überarbeiten damit sie auch bloß diese verdammte Null am Ende einfügen [...]
Daher lieber mit irgendwelchen finsteren Hacks den Platz am Stapelspeicher bestimmen, um schön unsichtbar zu entscheiden, wie wann was allokiert werden soll, oder irgendwelche dubiosen threadlokalen Memorypools basteln, die versteckt im Hintergrund ihr Unwesen treiben!? Ehrlich gesagt denk ich, dass das einem kohärenten, minimalen Konzept wirklich den Strick drehen würde :P Einfach ne 0 anhängen, würd mich da vergleichsweise kaum jucken. Außerdem ist das Problem ja nicht nur die WinAPI, sondern so ziemlich jede C-API, die's schon etwas länger gibt. Und die 0 ändert doch am Konzept nix, deine Stringops bekommen die eh nie zu Gesicht, sollte daher doch auch nur vergleichweise wenige Methoden betreffen!?
Krishty hat geschrieben:
dot hat geschrieben:Außerdem: Bist du dir sicher, dass die Allokation überhaupt das Problem ist?
Nö. Wenn ich Probleme hätte, müsste ich mir nicht über die Reinheit des großen Ganzen den Kopf zerbrechen :) Keine Probleme, keine Engpässe. Mir passt es nur nicht, dass etlicher Kleinkram einen WinAPI-Aufruf kostet, wo ich auch einfach von Stapel leben könnte.
Na dann leb doch einfach vom Stapel? Ich mein, wo ist eigentlich das Problem!? Der Stapel ist nunmal eben begrenzt, kannst du eben vielleicht zwei Rekursionsebenen weniger machen, bevors abkackt. Du wirst ja doch wohl kaum die Bibliothek von Alexandria am Stack allokieren wollen? ;)

EDIT: Es wär vielleicht sowieso keine schlechte Sache, deiner Stringklasse generell eine Allocator-Policy zu verpassen, wo dann eben für die relevanten Strings ein CStyleAllocator übergeben wird, der einfach die Kompatibilitäts-0 anhängt.
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von Krishty »

dot hat geschrieben:Daher lieber mit irgendwelchen finsteren Hacks den Platz am Stapelspeicher bestimmen, um schön unsichtbar zu entscheiden, wie wann was allokiert werden soll
Nee. Elegant fehlschlagen lassen. Sobald ein sauberes bad_alloc auf dem Weg ist, ist alles gut – und im optimalen Fall braucht es dafür nur die if-Abfrage mit dem Ergebnis einer Funktion, die ganz tief vergraben wird. Speicher ist ja überall begrenzt; egal, ob auf Stapel oder im Freispeicher … da kann ich dann mit umgehen; muss ich auch können.

Die Structured Exception ist, was mich wurmt … weil man sie nicht sauber verarbeitet kriegt. Sie bedeutet nicht nur bei jedem, der die optimierte Funktion am Ende nutzen will, zusätzlichen Aufwand zur Ausnahmebehandlung, sondern auch Wissen um die Interna der Funktion zwecks Stapelwiederherstellung – was ja ein absolutes No-Go ist. Und sie beißt sich zudem noch mit aller „gewöhnlichen“ Ausnahmebehandlung, von der ich mittlerweile ganz stark abhänge.

Wenn ich nun einfach mit der „Speicher ist eben irgendwann zuende, na und?“-Mentalität anfange, kommt irgendwann so ein Crysis-ähnlicher Bug: Überlangen String in eine INI eintragen, einem Server beitreten und ihn mitsamt der Teilnehmer abschießen, weil alle beteiligten Systeme nicht auf eine Stack-Overflow-Ausnahme vorbereitet sind. Deine Meinung von Leuten, die sowas in ihrer Software tolerieren, wird nicht höher sein als meine; darum muss ich dir nicht erst sagen, wie gern ich das vermeiden will. Darum suche ich verzweifelt einen Weg, einen Fehlschlag von alloca() prognostizieren zu können, bevor es meine Anwendung zerreißt – und die Prüfung, ob ich ausreichend Stapelspeicher zur Verfügung habe bevor ich da allokiere, scheint mir einfach am sinnvollsten.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von dot »

Na allokier Strings, die potentiell so lang werden können, doch einfach am Heap? Oder regel das ganze einfach über ne Obergrenze: Alles was länger ist als x, kommt auf den Heap, dann hast du in jedem Fall vorhersagbares Verhalten, was Speicherverbrauch am Stack angeht und du kannst x evtl. so wählen, dass das Allokieren im Vergleich zum Kopieren weniger ausmacht.

Kann man eine Structured Exception nicht normalerweise einfach mit nem __except fangen?
Benutzeravatar
BeRsErKeR
Establishment
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von BeRsErKeR »

Wie wärs denn mit _malloca? (Achtung: nicht malloc!)

Das ist quasi die secure-version von _alloca (ähnlich wie s_sprintf usw).


Auszug aus oben genannter Referenz:
_malloca allocates size bytes from the program stack or the heap if the request exceeds a certain size in bytes given by _ALLOCA_S_THRESHOLD. The difference between _malloca and _alloca is that _alloca always allocates on the stack, regardless of the size. Unlike _alloca, which does not require or permit a call to free to free the memory so allocated, _malloca requires the use of _freea to free memory. In debug mode, _malloca always allocates memory from the heap.

Weiterhin ist auf der Seite unten noch ein Beispiel, wie man Stack Overflows ggf. auch abfangen kann. ;)

Dabei ist _resetstkoflw() entscheidend, damit das Programm nicht abschmiert (repariert quasi den Stack). Falls das fehlschlägt, kannst du es wenigstens noch würdevoll abschmieren lassen. :D Die Dokumentation zu _resetstkoflw() bietet auch noch einige hilfreiche Informationen. So ist es sogar möglich mittels _set_se_translator() eine C++ Exception durch die Structured Exception zu erzeugen. Das ganze am Ende der _resetstkoflw() Dokumentation auch wieder mit Beispielen.

Letzteres ist natürlich auch mit der normalen _alloca Funktion möglich. _malloca bietet einfach noch etwas mehr Sicherheit und ermöglicht den automatischen Wechsel von Stack zu Heap.
Ohne Input kein Output.
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von Krishty »

dot hat geschrieben:Oder regel das ganze einfach über ne Obergrenze: Alles was länger ist als x, kommt auf den Heap, dann hast du in jedem Fall vorhersagbares Verhalten, was Speicherverbrauch am Stack angeht und du kannst x evtl. so wählen, dass das Allokieren im Vergleich zum Kopieren weniger ausmacht.
BeRsErKeR hat geschrieben:
_malloca allocates size bytes from the program stack or the heap if the request exceeds a certain size in bytes given by _ALLOCA_S_THRESHOLD.
Einen (mehr oder weniger festen) Grenzwert vorzuschreiben finde ich absolut töricht. Als ob ich endlos Speicher anfordern könnte, so lange ich es nur in kleinen Schritten täte …
dot hat geschrieben:Kann man eine Structured Exception nicht normalerweise einfach mit nem __except fangen?
BeRsErKeR hat geschrieben:Weiterhin ist auf der Seite unten noch ein Beispiel, wie man Stack Overflows ggf. auch abfangen kann. ;)
Ja, da gibt es nur ein enormes Problem: Ich darf _resetstkoflw() nicht während des Stack Unwindings aufrufen. Das bedeutet:
  • nicht von einem catch-Block aus, und
  • nicht von einer SEH-Translator-Funktion aus.
Bleiben nicht viele sinnvolle Möglichkeiten, _resetstkoflw() aufzurufen, oder?
  • in einem __except-Block (der schließt aber jede andere Form von Ausnahmebehandlung aus; d.h. sobald ich einen in meine Funktion schreibe, kann ich dort keine C++-Ausnahmen mehr werfen und es werden keine Destruktoren mehr aufgerufen);
  • nach dem catch-Block, der eine übersetzte Stapelüberlaufausnahme abgefangen hat – das bedeutet aber einerseits, dass ich die umliegenden Textteile mit Fehlerbehandlung schwemme, mit der sie garnichts zu tun haben dürften, und andererseits, dass ich das hinter jedem catch-Block in meinem gesamten Programm machen wüsste, weil ich ja nie weiß, ob das in dem try nicht irgendwann eine Funktion aufruft, die _alloca() oder _malloca aufruft.
Da kriege ich einen Tobsuchtanfall, dass die Scheißdinger nicht einfach NULL zurückgeben, falls sie auf die Guard Page treffen …

… und darum bin ich überzeugt, dass der beste Weg ist, erst gar keine Structured Exception zu riskieren sondern von Hand bad_alloc zu werfen, falls man keinen Erfolg prognostizieren kann.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von kimmi »

Kennst du Stack-Allokatoren? Die wären hier eventuell sehr performant. Du baust dir deinen eigenen Stack, der initial auf dem Heap angelegt wird. Dann benutzt du den wie gewohnt, nur dass du halt den reservierten Bereich wiederverwendest. Überschreitest du die Größe des Buffern, wird realloziert. Google ist da dein Freund, willst du mehr erfahren.

Gruß Kimmi
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von dot »

Krishty hat geschrieben:Einen (mehr oder weniger festen) Grenzwert vorzuschreiben finde ich absolut töricht. Als ob ich endlos Speicher anfordern könnte, so lange ich es nur in kleinen Schritten täte …
Ich fände es töricht, ohne Grenzwert zu arbeiten. Denn dann laufst du doch erst wieder in genau das oben beschriebene Problem!? Grenzwert bedeutet doch nicht, dass du so tust, als könntest du endlos Speicher anfordern. Eigentlich genau das Gegenteil. Strings die ihren Buffer auf den Stack legen, haben zwangsweise lokalen Scope (btw: Denk dran, sicherzustellen, dass niemand so einen String mit new anlegen kann, denn das wäre fatal und potentiell eine riesige Sicherheitslücke?). D.h. sie sind zwangsweise zur Compiletime bekannt. D.h. solange ihr Speicherverbrauch bounded ist (und nur solange), ist der ganze Speicherverbrauch am Stack bounded. Ganz so, als wären es einfache lokale char-Buffer...

EDIT: Das __except bräuchtest du doch auch nur in der Funktion, die den Speicher reserviert. Und die kann ja dann einfach nullptr returnen. Wobei ich generell finde, dass die Stackoverflow Exception da das absolut falsche Maß ist...
Zuletzt geändert von dot am 08.09.2011, 22:30, insgesamt 7-mal geändert.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von dot »

Wo ich nochmal drüber nachdenke, würd ich mal behaupten, dass die beiden Systeme eigentlich mehr oder weniger gleichwertig sind. Ob ich jetzt dafür sorge, dass auf dem Stack noch mindestens x Byte übrig bleiben, oder dafür sorge, dass niemals mehr als N - x Byte auf dem Stack allokiert werden, macht am Ende keinen Unterschied. Beide Varianten unterscheiden sich nur im "Defaultverhalten", wenn man so will. Bei deiner Variante kann ich weiter oben von Haus aus mehr Speicher allokieren, was dazu führt, dass ich in tieferen Ebenen auf den Heap angewiesen bin. Mit einer fixen Obergrenze kann ich auf jeder Ebene gleich viel vom Stack nehmen. Ich würde mal argumentieren, dass letzteres im Allgemeinen sogar sinnvoller ist, wenn man bedenkt, dass, z.B. bei der Bearbeitung rekursiver Strukturen, die tieferen Ebenen eher ein Performancefaktor sind als die höheren. Sobald ich auf jeder Call-Ebene bestimmen kann, wieviel Platz bleiben muss, bzw. wieviel maximal allokiert werden darf (was wohl eine sinnvolle Option sein wird, um für verschiedene Anwendungsfälle optimieren zu können), sind beide Systeme sogar wirklich völlig Äquivalent.
Zuletzt geändert von dot am 08.09.2011, 22:27, insgesamt 1-mal geändert.
Helmut
Establishment
Beiträge: 237
Registriert: 11.07.2002, 15:49
Wohnort: Bonn
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von Helmut »

Schonmal daran gedacht, was passiert, wenn auf dem Stack noch x Bytes frei sind und du dann zufällig einen String der Länge x-1 bearbeitest? Beim nächsten Funktionsaufruf kracht es dann, und dann einen Stack overflow ordentlich abzufangen ist praktisch unmöglich. Und selbst wenns möglich wäre, um die Programmausführung irgendwie fortzusetzen müsstest du irgendwie alls Strings auf dem Stack in den Heap verfrachten oder den Stack vergrößern. Beides ist praktisch unmöglich. Tollerweise treten solche Bugs dann auch meistens erst beim Enduser auf, der dein Programm dann sicher in gewisser Hinsicht mit Crysis vergleichen wird ;)

Dynamische Arraygrößen wurden in C++ nicht ohne Grund weggelassen. Benutz entweder fixe Größen auf dem Stack, dann kommt jeder Stackoverflow beim Testen ans Licht, oder halt den Heap. Man kann auch eine konstante Größe auf dem Stack reservieren, und, falls das zu klein ist, auf den Heap ausweichen. Den Aufwand dürfte das aber in den meisten Fällen nicht wert sein.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von dot »

Helmut hat geschrieben:Schonmal daran gedacht, was passiert, wenn auf dem Stack noch x Bytes frei sind und du dann zufällig einen String der Länge x-1 bearbeitest? [...]
[...] Benutz entweder fixe Größen auf dem Stack, dann kommt jeder Stackoverflow beim Testen ans Licht, oder halt den Heap.
My point exactly ;)
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von Krishty »

Reden wir komplett aneinander vorbei? :)
dot hat geschrieben:D.h. sie sind zwangsweise zur Compiletime bekannt.

EDIT: Das __except bräuchtest du doch auch nur in der Funktion, die den Speicher reserviert.
Das ist doch beides völlig unvereinbar mit alloca() … nimm diese C-Funktion, die einen nicht-nullterminierten String entgegennimmt und eine Null anhängt, um ihn via WinAPI an den Debugger zu schicken:

void outputPascalString(char const * string, size_t len) {
    char tempArray[len + 1];
    memcpy(tempArray, string, len);
    tempArray[len] = 0;
    OutputDebugString(tempArray);
}


Das geht in C++ nicht, weil es keine Variable-Length Arrays erlaubt. Ich möchte mir nun mit alloca() behelfen:

void outputPascalString(char const * string, size_t len) {
    char * tempArray = (char *)alloca(len + 1);
    ::memcpy(tempArray, string, len);
    tempArray[len] = 0;
    ::OutputDebugString(tempArray);
}


So. Da steht nichts zur Kompilierzeit bekannt, und da aller via (m)alloca() geschnappter Speicher beim Verlassen der Funktion wieder futtsch ist, kann ich auch schlecht eine Funktion drumherum bauen, die den Überlauf abfängt und nullptr zurückgibt. Sind wir soweit konform?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [WinAPI] Größe des Stapelspeichers bestimmen

Beitrag von dot »

Krishty hat geschrieben:Da steht nichts zur Kompilierzeit bekannt, [...]
Doch, da ist zur Compilezeit bekannt, dass es exakt einen Buffer mit Namen tempArray gibt. Wenn der Speicherverbrauch von tempArray am Stack nun nach oben begrenzt wäre, dann wäre das Verhalten des Programmes dahingehend predictable. In deinen Beispielen ist das natürlich nicht der Fall, daher leiden beide natürlich am Crysis-Syndrom.
Krishty hat geschrieben:[...] und da aller via (m)alloca() geschnappter Speicher beim Verlassen der Funktion wieder futtsch ist, kann ich auch schlecht eine Funktion drumherum bauen, die den Überlauf abfängt und nullptr zurückgibt.
Das kapier ich nicht :?

Code: Alles auswählen

void* stack_alloc(size_t size)
{
  __try
  {
    return _alloca(size);
  }
  __except (...)
  {
    ...
  }
  return nullptr;
}
Zuletzt geändert von dot am 09.09.2011, 18:57, insgesamt 1-mal geändert.
Antworten