powered by simpleCommunicator - 2.0.37     © 2025 Programmizd 02
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Stack Overflow
19 сообщений из 19, страница 1 из 1
Stack Overflow
    #40095483
Фотография Maxim Rusov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Почему при Subj программа просто слетает, вместо того чтобы выдать сообщение и работать дальше?
...
Рейтинг: 0 / 0
Stack Overflow
    #40095485
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Потому что невозможно работать без свободного места в стэке, это слишком важная
часть памяти.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Stack Overflow
    #40095488
Фотография Maxim Rusov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Но исключение предусмотрено и оно генерируется. Если бы было невозможно - поставили бы Halt.
Почему не происходит откат по стеку с его освобождением?
...
Рейтинг: 0 / 0
Stack Overflow
    #40095495
Barmaley57
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Maxim Rusov
Почему не происходит откат по стеку с его освобождением?
А как его откатывать?
...
Рейтинг: 0 / 0
Stack Overflow
    #40095500
Фотография Maxim Rusov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Как обычно. Если я на N-й глубине вложенности сгенерирую исключение - все корректно откатывается. А если на N+1 возникает Stack Overflow - краш. Почему?
...
Рейтинг: 0 / 0
Stack Overflow
    #40095512
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Maxim RusovА если на N+1 возникает Stack Overflow - краш.
Какой именно краш?
...
Рейтинг: 0 / 0
Stack Overflow
    #40095513
Фотография Maxim Rusov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Процесс слетает
...
Рейтинг: 0 / 0
Stack Overflow
    #40095523
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Молча? Так не бывает. Хоть что-то да должно остаться в Windows Events.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Stack Overflow
    #40095528
Barmaley57
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Maxim Rusov
Как обычно. Если я на N-й глубине вложенности сгенерирую исключение - все корректно откатывается. А если на N+1 возникает Stack Overflow - краш. Почему?
Фрейм исключения пушится в стек. А стек закончился. При нормальном раскладе ОСь раскручивает цепочку EXCEPTION_RECORD's с помощью RtlUnwind. Но этой функции нужно за что-то зацепиться. А это что-то не влезло в стек...
...
Рейтинг: 0 / 0
Stack Overflow
    #40095531
Фотография Maxim Rusov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ограниченно понял. Оказывается, когда попадаем в корневой Except стек еще не освободился, и мы получаем повторную ошибку при обработке исключения. Стек освобождается при завершении Except блока в DoneExcept (x86)

Т.е. ошибку можно обойти как то так:

Код: pascal
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.
  procedure LocRecursion(aLevel :Integer);
  begin
//  Trace('%d', [aLevel]);
    if aLevel > 0 then
      LocRecursion(aLevel - 1);
  end;


  procedure TAboutBox1.Button1Click(Sender: TObject);
  var
    vStackOverflow :Boolean;
  begin
    vStackOverflow := False;
    try
      Trace('Run...');
      LocRecursion(100000);
      Trace('  done');
    except
      on E :EStackOverflow do
        vStackOverflow := True;
      on E :Exception do
        raise;
    end;

    if vStackOverflow then
      raise EStackOverflow.CreateRes(@SStackOverflow);
  end;



Но при повторном вызове - все плохо, Stack Overflow уже не возникает, возникает AV и программа все-таки слетает. Странно все это...
...
Рейтинг: 0 / 0
Stack Overflow
    #40095542
Фотография Maxim Rusov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Нашел такое:

https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/resetstkoflw?view=msvc-160

Интересно, это может помочь? И как это можно из Delphi вызвать?...
...
Рейтинг: 0 / 0
Stack Overflow
    #40095573
GunSmoker
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Тут надо понять принцип возбуждения Stack Overflow. У стека потока в конце ставится специальная страница, которая имеет атрибут доступа GUARD. Когда поток исчерпает свободное место в стеке - он попытается записать в эту страницу. В результате страница будет выделена обычным образом, а атрибут GUARD будет перенесён на следующую страницу. Но если эта страница была последней - то атрибут GUARD переносить некуда, ибо там будет зарезервированное место с NO_ACCESS. И когда такое случается - возбуждается исключение Stack Overflow.

При этом картина следующая: стек выделен полностью и ограничен с двух сторон страницами с NO_ACCESS. Страницы с GUARD нет. А управление передаётся на обработчик Stack Overflow, который начинает работать в только что выделенной странице.

Предполагается, что приложение продолжит выполнение и обработает Stack Overflow. В типичных приложениях Delphi это приведёт к ShowMessage('Stack Overflow'). Однако, если для обработки исключения приложению потребуется больше памяти на стеке, чем там осталось (одна страница, 4 кб) - то в итоге оно попробует записать в область памяти с NO_ACCESS. В ответ на это система в конечном итоге закроет приложение извне.

Упражнение: что не так с Delphi-вым GetModuleName с его статичным буфером в 520 байт для имени?

Аналогичное справедливо, если исключение Stack Overflow было обработано, но приложение продолжило выполняться дальше и снова попало в ситуацию, когда должно было бы возбуждаться второе Stack Overflow. Но поскольку GUARD-страницы больше нет, то и возбуждать нечего. В итоге приложение снова упадёт с фатальным Access Violation при попытке записи в NO_ACCESS.

Аналог _resetstkoflw на Delphi выглядит примерно так:
Код: pascal
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.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
function _resetstkoflw: Boolean;
const
  MIN_STACK_REQ_WINNT = 2; // in pages

var
  pStack, pStackBase, pMaxGuard, pMinGuard: PAddress;
  mbi: TMemoryBasicInformation;
  si: TSystemInfo;
  PageSize: DWORD;
  RegionSize: DWORD;
  flOldProtect: DWORD;
  StackSizeInBytes: ULONG;
begin
  Result := False;
  pStack := GetStackPointer;

  // Find the base of the stack.
  if VirtualQuery(pStack, mbi, SizeOf(mbi)) = 0 then
    Exit;

  pStackBase := PAddress(mbi.AllocationBase);

  GetSystemInfo(si);
  PageSize := si.dwPageSize;
  RegionSize := 0;
  StackSizeInBytes := 0;               // Indicate just querying
  if Pointer(@GSetThreadStackGuarantee) = nil then
  begin
    GSetThreadStackGuarantee := GetProcAddress(LibKernel32, 'SetThreadStackGuarantee');
    if not Assigned(@GSetThreadStackGuarantee) then
      GSetThreadStackGuarantee := Pointer(1);
  end;
  if AssignedEx(Pointer(@GSetThreadStackGuarantee)) then
  begin
    if GSetThreadStackGuarantee(StackSizeInBytes) then
      RegionSize := StackSizeInBytes;
  end;

  {$R-}
  {$Q-}
  // Silence prefast about overflow/underflow
  RegionSize := PtrUInt(RegionSize + PageSize - 1) and (not PtrUInt(PageSize - 1));
  {$IFDEF OVERFLOWCHECKS_ON}{$Q+}{$ENDIF}
  {$IFDEF RANGECHECKS_ON}{$R+}{$ENDIF}

  //
  // If there is a stack guarantee (RegionSize nonzero), then increase
  // our guard page size by 1 so that even a subsequent fault that occurs
  // midway (instead of at the beginning) through the first guard page
  // will have the extra page to preserve the guarantee.
  //

  if RegionSize <> 0 then
    RegionSize := RegionSize + PageSize;

  if RegionSize < MIN_STACK_REQ_WINNT * PageSize then
    RegionSize := MIN_STACK_REQ_WINNT * PageSize;

  //
  // Find the page(s) just below where the stack pointer currently points.
  // This is the highest potential guard page.
  //

  {$R-}
  {$Q-}
  pMaxGuard := Pointer((PtrUInt(pStack) and (not (PtrUInt(PageSize) - 1))) - RegionSize);
  {$IFDEF OVERFLOWCHECKS_ON}{$Q+}{$ENDIF}
  {$IFDEF RANGECHECKS_ON}{$R+}{$ENDIF}

  //
  // If the potential guard page is too close to the start of the stack
  // region, abandon the reset effort for lack of space.  Win9x has a
  // larger reserved stack requirement.
  //

  pMinGuard := pStackBase + PageSize;

  if pMaxGuard < pMinGuard then
    Exit;

  // Set the new guard page just below the current stack page.

  if (VirtualAlloc(pMaxGuard, RegionSize, MEM_COMMIT, PAGE_READWRITE) = nil) or
     (not VirtualProtect(pMaxGuard, RegionSize, PAGE_READWRITE or PAGE_GUARD, flOldProtect)) then
    Exit;

  Result := True;
end;


Она может помочь во втором случае, но не может помочь в первом, потому что она, по сути, просто восстанавливает GUARD-страницу, но только при условии, что для этого есть место .

Дело в том, что _resetstkoflw можно использовать только следующим образом:
Код: pascal
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.
NeedReset := False;
try
  Recursive(Recurse);
except
  on EStackOverflow do
  begin
    // Здесь нужно использовать минимум стека,
	// потому что у нас есть всего одна страница (4 кб).
    // Тут _resetstkoflw вызывать нельзя, 
	// потому что стек ещё не раскручен.
	// В настоящее время мы выполняемся в том месте,
	// куда нужно поместить GUARD-страницу, 
	// т.е. сейчас это сделать невозможно.
    // Поэтому вместо этого просто отметимся.
    NeedReset := True;
  end;
end;

// А здесь стек уже отмотался назад 
// и у нас есть гораздо больше места.
// Последняя страница теперь свободна 
// и её можно конвертировать в GUARD.
if NeedReset and (ac >= 2) then
  Result := _resetstkoflw;

// Если _resetstkoflw не смог создать GUARD-страницу,
// то дальнейшее выполнение небезопасно.
// Нужно максимально быстро корректно выйти из приложения.
if not Result then
begin
  Halt(3);
  Exit;
end;
...
Рейтинг: 0 / 0
Stack Overflow
    #40095575
Фотография Maxim Rusov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
GunSmoker,

Да, спасибо. Уже сам спортировал _resetstkoflw, а нужно было просто подождать :)

Такой код теперь работает стабильно:

Код: pascal
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.
  procedure LocRecursion(aLevel :Integer);
  begin
//   Trace('%d', [aLevel]);
    if aLevel > 0 then
      LocRecursion(aLevel - 1);
  end;


  procedure TAboutBox1.Button1Click(Sender: TObject);
  var
    vStackOverflow :Boolean;
  begin
    vStackOverflow := False;
    try
      Trace('Run...');
      LocRecursion(100000);
      Trace('  done');
    except
      on E :EStackOverflow do
        vStackOverflow := True;
      on E :Exception do
        raise;
    end;

    if vStackOverflow then begin
      ResetStkOflw;
      raise EStackOverflow.CreateRes(@SStackOverflow);
    end;
  end;
...
Рейтинг: 0 / 0
Stack Overflow
    #40095577
O_O_P
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
А почему при старте приложения не вызывать SetThreadStackGuarantee со значением, например, 16K?
MSDN прямо в первом предложении к описанию функции как бы намекает, что при Stack Overflow столько будет доступно как раз на вызов исключения.
Или нет?
...
Рейтинг: 0 / 0
Stack Overflow
    #40095578
Фотография Maxim Rusov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
O_O_P,

Но ResetStkOflw все равно делать нужно, там дальше написано
...
Рейтинг: 0 / 0
Stack Overflow
    #40095740
Фотография Maxim Rusov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Кстати, до кучи

GunSmoker
Дело в том, что _resetstkoflw можно использовать только следующим образом:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
try
  Recursive(Recurse);
except
  on EStackOverflow do
  begin
    // Здесь нужно использовать минимум стека,
	// потому что у нас есть всего одна страница (4 кб).
    // Тут _resetstkoflw вызывать нельзя, 
	// потому что стек ещё не раскручен.
	// В настоящее время мы выполняемся в том месте,
	// куда нужно поместить GUARD-страницу, 
	// т.е. сейчас это сделать невозможно.
  end;
end;


Отквоченное относится только к x86. В x64 стек освобождается и _resetstkoflw в except вызывать можно.
...
Рейтинг: 0 / 0
Stack Overflow
    #40095783
GunSmoker
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Да, к счастью в x64 - табличные исключения, указатели на обработчики исключкений хранятся в отдельной секции файла, не в стеке, поэтому там нет никаких проблем размотать стек сразу. Но в x86 указатели на обработчики исключений находятся в стеке, поэтому стек отмотать нельзя, пока обработка исключения не будет завершена. В противном случае (если стек размотать сразу) обработчик исключения начнёт выполняться и затрёт указатели на следующие обработчики.
...
Рейтинг: 0 / 0
Stack Overflow
    #40095806
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А вроде бы везде пишется, что и там и там Delphi применяет SEH, а не
какой-нибудь SJLJ.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Stack Overflow
    #40095810
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimitry Sibiryakov,

он и есть, просто для 32-битного режима стандарта нет и все делают как хотят
борланд исторически стек использовал для хранения цепочек try

в 64-битном формате мелкософт эту "недоработку" устранил, впрочем, как уже заведено "через одно место"
...
Рейтинг: 0 / 0
19 сообщений из 19, страница 1 из 1
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Stack Overflow
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]