Range check error как исправить delphi

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, JPEG;
 
type
  pt = ^z;  z = Record elem:integer; ss: pt; End;
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    Button5: TButton;
    CheckBox1: TCheckBox;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    {procedure CheckBox1Click(Sender: TObject);}
         private
    { Private declarations }
  public
 
     { Public declarations }
  end;
 
var
  Form1: TForm1;
  kol,st: integer;
  Block: array of TImage;
  Value: array of TLabel;
  delElem: integer;
  d,nach: pt;
   j: byte;
  otstupLabel,
  otstupImage: integer;
 
implementation
 
uses Unit2;
 
{$R *.dfm}
 
 
Const A: Array[1..8] of integer =(1,2,3,4,5,6,7,8);
 
 
 
procedure TForm1.Button1Click(Sender: TObject);
 
begin
 
             {*****!!!!Âûâîä!!!!*****}
 
 While ((nach <> Nil)and(kol<>0)) do   {Ïîêà ñòåê íå îïóñòååò, âûòàëêèâàåì ýëåìåíòû}
 Begin
 d:=nach;  {Óêàçàòåëü q,"îòñòàþùèé" îò t,íóæåí äëÿ óíè÷òîæåíèÿ}
 Memo1.Lines.Add(IntToStr(nach^.elem));
 
 begin
 
   Block[st]:=TImage.Create(Self);
     with Block[st] do
         begin
            Parent:=Form1;
            Picture.LoadFromFile('Imageimg.bmp');
            AutoSize:=True;
            Top := 280;
            Left := otstupImage;
            otstupImage:=otstupImage+width;
           end;
 
     Value[st]:=Tlabel.Create(Self);
       with Value[st] do
            begin
               Parent:=Form1;
               Caption:=IntToStr(nach^.elem);
               Top :=295;
               Left :=otstupLabel;
               otstupLabel:=otstupImage+40;
              end;
 end;
 nach:= nach^.ss; {Âûâîä è ïðîäâèæåíèå ê êîíöó ñïèñêà}
 kol:=kol-1;
 st:=st+1;
 
 Dispose (d);        {Îñâîáîæäàåì ïàìÿòü, êîòîðóþ çàíèìàëà çàïèñü}
  End;
 
end;
 
 
 
procedure TForm1.Button2Click(Sender: TObject);
begin
Memo1.clear;
end;
 
 
procedure TForm1.Button5Click(Sender: TObject);
begin
Form2.Show;
end;
 
 
procedure TForm1.FormActivate(Sender: TObject);
begin
kol:=0;
st:=1; {ñ÷åò÷èê äëÿ ìàññèâîâ Image è Label}
Memo1.Clear;
otstupImage:= 25;
otstupLabel:= 65;
nach:= Nil;      {Çàíîñèì â óêàçàòåëü çíà÷åíèå ïóñòîé ññûëêè}
For j:= 1 to 8 do   {Çàíîñèì â ñòåê 8 ÷èñåë - ýëåìåíòîâ ìàññèâà A}
 
 Begin
    New(d);
    d^.elem:= A[j];
    d^.ss:= nach;
    nach:= d;
    kol:=kol+1; {êîëè÷åñòâî ýëåìåíòîâ ñòåêà}
 End;
label1.Caption:=IntToStr(kol);
end;
 
 
{
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if checkbox1.Checked = True then
for st:=1 to  index  do
           begin
 
                   Block[st].Visible:=False;
                   Value[st].Visible:=False
 
                            else
 
                   Block[st].Visible:=True;
                   Value[st].Visible:=True;
 
           end;
end;  }
 
 
end.

After spending a week checking and fixing my program for memory leaks through FastMM4, I finally test ran my program on a different PC. Now, I am getting “Range Check Error.” I have spent hours researching online about this, but none of them seem to give me what I am looking for. My program was complied with the Runtime Error option Range Check. So, I know that’s why I am getting the error, but I needed to know exactly why the error is raised.

The program was compiled on XP with Delphi 7.0. The testing PC is a Windows 7. As soon as it starts up, my program begins to communicate through serial port and then followed by “Range Check Error” message boxes. When I stop the serial communication, there are no “Range Check Error” boxes. What does this mean and how do I go about resolving it? I am looking for simple strategy. I know I could spend days checking line by line.

“Range Check Error” caused by improper assignment of values or accessing inaccessible index of an array. Am I correct?

asked Mar 28, 2011 at 16:11

ThN's user avatar

4

Your understanding of range check errors is correct. They arise when you access an array outside it’s bounds. For example:

type
  TFixedArray = array [0..41] of Integer;
var
  a: TFixedArray;
begin
  a[42] := 1+2;//!! this is a range check error !!
end;

Or for a dynamic array:

var
  a: array of Integer;
begin
  SetLength(a, 666);
  a[665] := 12;//this is fine
  a[666] := 42;//!! this is a range check error !!
end;

I’ve illustrated this with assignment, but reading an array with an index out of bounds will also produce a range error.

The range error should report an address at which it occurs which you can then translate into a code location with your map file. Even better would be if you were using madExcept or some such tool.


UPDATE

Prompted by Ken, the documentation states what is affected by the range checking option as follows:

In the {$R+} state, all array and
string-indexing expressions are
verified as being within the defined
bounds, and all assignments to scalar
and subrange variables are checked to
be within range.

answered Mar 28, 2011 at 16:15

David Heffernan's user avatar

David HeffernanDavid Heffernan

599k42 gold badges1060 silver badges1478 bronze badges

6

Having read other information about the concept of “range check error”, I believe that the reason for causing the “range check error” in this scenerio is that:
the variable assigning to access the serial port the program reads is an 16-bytes(or smaller) type, and the serial port the program reads exceeds the limitation of the variable.
Notice that [When I stop the serial communication, there are no “Range Check Error” boxes.], this should make all things clear.

answered Oct 27, 2017 at 8:00

Edward's user avatar

11 minutes ago, david_navigator said:

so I think what you’re saying is that at the time InQue runs (count) there might be 100 bytes waiting to be read, but by the time ReadString gets called, that might be 150 bytes ?

That was an example, but your exception was raised because cbInQue was 0 to read while ClearCommError was success, in this one particular case that code will raise exception.

13 minutes ago, david_navigator said:

It’s probably never risen it’s head as a problem because this code is simply processing short barcodes, so it’s 10 bytes, then a pause whilst the human does something and then another 10 bytes etc,

OK, so you want 10 bytes per operation, then i suggest read 100 and if there is only 10 then you will get 10 or more or even less in case something out of ordinary went down and these data was hold somewhere and two reads should be performed, in all cases the best design should be like this

1) a buffer with enough size and counter to indicate number of bytes with in that buffer

2) watch for signal read received

3) issue read with length length(buffer) – counter and place the data on buffer[counter], not from the start but from counter 

4) loop and process each 10 in that buffer, any left over move them to the beginning of buffer and adjust the counter.

5) notice that counter will never be more than 10-1 at this step while it might be any value less than Length(Buffer)

6) goto 2

Now the above is design will not cause overflow, but as i said you can adjust your code after you understand how I/O operations works, one thing to remember size’s is unreliable for most in not all I/O operations, size’s only a fact after an operation is done and you got a success OS call.

Trying to get AsyncPro running in D2010. Using the 5.00 version from Source Forge.

The AsyncPro code (in OOMisc.pas) below is failing with a range check errror on the MakeLong line below. I don’t have a clue how to start debugging this.

Does anyone have ASyncPro running in D2010, or have some insight into what might be going on below? A posting by me on the SourceForge yielded no responses.

function SafeYield : LongInt;
  {-Allow other processes a chance to run}
var
  Msg : TMsg;
begin
  SafeYield := 0;
  if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then begin
    if Msg.Message = wm_Quit then
      {Re-post quit message so main message loop will terminate}
      PostQuitMessage(Msg.WParam)
    else begin
      TranslateMessage(Msg);
      DispatchMessage(Msg);
    end;
    {Return message so caller can act on message if necessary}
    SafeYield := MAKELONG(Msg.Message, Msg.hwnd);  // Range Check Error on this line!
  end;
end;

TIA

asked Dec 9, 2010 at 4:01

RobertFrank's user avatar

RobertFrankRobertFrank

7,32211 gold badges53 silver badges98 bronze badges

It seems that you compile the code with range checking on:

{$R+}
function Test(A, B: LongWord): LongInt;
begin
  Result:= MakeLong(A,B);
// Project .. raised exception class ERangeError with message 'Range check error'.
end;

You can switch range checking off to get rid off the runtime error, but the result of

SafeYield := MAKELONG(Msg.Message, Msg.hwnd)

is incorrect if one of the arguments (or both) is above 2^16 – 1.

Looks like the code was ported from 16-bit AsyncPro version without change to 32-bit version and the bug existed where through all 32-bit AsyncPro versions.

answered Dec 9, 2010 at 5:34

kludg's user avatar

Seeing as how MAKELONG takes two parameters of type Word (16bits), and Msg.Message and Msg.HWnd are both 32bits it is not surprising that you’re getting range-check errors. In general, window messages are < $8000 so I doubt that value is the problem. However, the integral value of an HWnd can be all over the map and is certainly > $FFFF quite often. Because of this the above code doesn’t really make sense, except that it appears to be a long-ago left over artifact from the 16bit version.

Since range-checking is enabled it is clearly highlighting the fact that the above code needs a bit of a re-think. In Win32, you cannot fit a message value and window handle into 32bits anymore.

I hope I’ve given you a few hints on how to proceed. Without taking into account the code that calls this function, it is not possible to suggest an alternative implementation.

answered Dec 9, 2010 at 5:09

Allen Bauer's user avatar

Allen BauerAllen Bauer

16.6k2 gold badges56 silver badges74 bronze badges

I would echo Allen’s comment – but go further.
If you look at how the code is being used (Look at DelayTicks also in OoMisc) callers either assume that the return value is unimportant or is JUST the message. Adding the Msg.hwnd to the number is not just not going to work, it is also not what the callers are expecting.

repeat
  if Yield then
    Res := SafeYield;
until (**Res = wm_Quit**) or TimerExpired(ET);

This code is expecting a message only.

I would change the line

 SafeYield := MAKELONG(Msg.Message, Msg.hwnd);

to

 SafeYield := Msg.Message;

answered Jan 10, 2011 at 22:35

Neil's user avatar

NeilNeil

1,5759 silver badges14 bronze badges

(1) That code is a Message Pump, and

(2) (in context) it is protected by a R- compiler directive. Range checking is turned off: {$R- No range checking} in AwDefine.inc

So (1) If some other message causes the code to stop, this is where it will be when the message goes through, and

(2) The range check error does not come from here.

This suggests that an asyncronous process is causing a range check exception or modal message. In the version of Delphi I am working with, range check errors (and list index messages) do not give any source/debug information, so I can only suggest that the error may be linked to an asynchronous Comm event, or even a got-focus/lost-focus/activate/paint event.

answered Sep 3, 2013 at 8:52

david's user avatar

daviddavid

2,4141 gold badge20 silver badges33 bronze badges

Сегодня я хочу рассмотреть различные настройки проектов Delphi, которые могут влиять на отладку приложений.

О чём идёт речь

Сначала, давайте посмотрим на них: открываем Project/Options. Нас будут интересовать вкладки Compiling и Linking (в старых версиях Delphi они назывались Compiler и Linker):

Вкладка Compiling

Вкладка Compiling в D2009

Вкладка Linking

Вкладка Linking в D2009

На вкладке «Compiler» нас будут интересовать опции «Stack Frames», группа «Debug information», «Local Symbols» и «Symbol reference info», «I/O Checking», «Overflow checking» и «Range checking». На «Linking» – «Map file», «Debug Information» (известная в предыдущих версиях Delphi как «Include TD32 debug info») и «Include remote debug symbols».

Давайте посмотрим, за что отвечают эти опции. А затем – как их лучше бы всего расставить. При этом мы будем рассматривать такие ситуации: обычное приложение и приложение с механизмом диагностики исключений.
Кроме того, настройки проекта могут отличаться, компилируете ли вы приложение для себя или для распространения. В новых версиях Delphi появились профили настроек (Debug и Release, соответственно). Вы можете задать свой набор настроек в каждом профиле, а затем переключаться между ними. В старых Delphi есть только один профиль настроек и вам нужно менять каждую настройку вручную.

Напомним, что при смене любой из опций необходимо сделать полный Build проекту (а не просто Compile).

Что означают эти опции?

Самыми важными настройками являются группа опций «Debug information», «Local Symbols» и «Symbol reference info».

Программа представляет собой набор машинных команд. Текст программы представляет собой текстовый файл. Вопрос: как отладчик узнаёт, когда надо остановиться, если вы поставили бряк на строку в тексте? Где же соответствие между текстовым файлом и набором байт в exe-файле? Вот для такой связи и служит отладочная информация. Это, грубо говоря, набор инструкций типа: «машинные коды с 1056 по 1059 относятся к строке 234 модуля Unit1.pas». Вот с помощью такой информации и работает отладчик. Указанные выше опции отвечают за генерацию отладочной информации для ваших модулей.

Отладочная информация сохраняется вместе с кодом модуля в dcu-файле. Т.е. один и тот же Unit1.pas может быть скомпилирован как с отладочной информацией, так и без неё – в разные dcu файлы. Отладочная информация увеличивает время компиляции, размер dcu-файлов, но не влияет на размер и скорость работы полученного exe-файла (т.е. отладочная информация не подключается к exe-файлу) (*).

Бывают ситуации, когда наличие отладочной информации в файле или (хотя бы) рядом с файлом является необходимым. Например, если вы выполняете удалённую отладку или отладку внешнего процесса. Или если вам нужен читаемый стек вызовов в вашем средстве диагностики исключений.

Подключение отладочной информации к приложению осуществляется несколькими способами: либо это опции проекта (а именно: «Map File», «Debug information» (Linker)/«Include TD32 Debug info» или «Include remote debug symbols»), либо это возможности всевозможных экспертов (типа EurekaLog, JCL или madExcept), которые добавляют отладочную информацию в программу в своём формате.

Итак, опции отладочной информации:

  • «Debug information» (директива {$D+} или {$D-}) – это собственно и есть отладочная информация. Т.е. соответствие между текстом программы и её машинным кодом. Вы должны включить эту опцию, если хотите ставить бряки, выполнять пошаговую отладку, а также иметь стек с именами для своего кода. Часто эту опцию включают автоматически различные эксперты типа EurekaLog.
  • «Local symbols» (директива {$L+} или {$L-}) – является дополнением к отладочной информации. Она отвечает за соответствие между данными в exe-файле и именами переменных. Если вы включаете эту опцию, то отладчик позволит вам просматривать и изменять переменные. Также окно «Call Stack» будет способно отражать переданные в процедуры параметры.
  • «Reference info» – это дополнительная информация для редактора кода, которая позволяет ему отображать более подробную информацию об идентификаторах. Например, где была объявлена переменная.

Эти три опции тесно связаны и обычно нет смысла включать/выключать их по одной.

  • «Use Debug DCUs» – эта опция переключает сборку вашей программы с отладочными модулями Delphi или с обычными. Если вы внимательно посмотрите на установку Delphi, то увидите, что pas файлы из папки Source никогда не используются для компиляции – а только для отладки. Для компиляции же используются уже готовые dcu-файлы из папки Lib. Это уменьшает время компиляции. Поскольку dcu могут быть скомпилированы с отладочной информацией и без неё, то в папке Lib есть два набора dcu-файлов: обычные и отладочные. Переключая эту опцию, вы указываете, какие из них использовать. Если вы выключите эту опцию, то не сможете заходить по F7 в стандартные функции и методы Delphi (т.к. для них будет отсутствовать отладочная информация). Также при выключенной опции вы не будете видеть информацию о стандартном коде в стеке вызовов.
  • «Stack Frames» – эта опция отвечает за генерацию стековых фреймов. Если опция выключена, то стековый фрейм не генерируется без необходимости. Если она включена -то фрейм генерируется всегда. Стековые фреймы используются при построении стека вызовов по фреймам (построение методом raw-сканирование не нуждается в стековых фреймах). В обычном приложении стековые фреймы генерируются практически всегда (**).
  • «Range checking» – служит помощником в поиске проблем при работе, например, с массивами. Если её включить, то для любого кода, который работает с массивами и строками, компилятор добавляет проверочный код, который следит за правильностью индексов. Если при проверке обнаруживается, что вы вылезаете за границы массива, то будет сгенерировано исключение класса ERangeError. При этом вы можете идентифицировать ошибку обычной отладкой. Если же опция выключена, то никакого дополнительного кода в программу не добавляется. Включение опции немного увеличивает размер программы и замедляет её выполнение. Рекомендуется включать эту опцию только в отладочной версии программы.
  • «Overflow checking» – похожа на опцию «Range checking», только проверочный код добавляется для всех арифметических целочисленных операций. Если результат выполнения такой операции выходит за размерность (происходит переполнение результата), то возбуждается исключение класса EIntOverflow. Пример – к байтовой переменной, равной 255, прибавляется 2. Должно получиться 257, но это число больше того, что помещается в байте, поэтому реальный результат будет равен 1. Это и есть переполнение. Эта опция используется редко по трём причинам. Во-первых, самый разный код может рассчитывать на то, что эта опция выключена (часто это различного рода криптографические операции, подсчёт контрольной суммы и т.п., но не только). В связи с этим при включении этой опции могут начаться совершенно различные проблемы. Во-вторых, в обычных ситуациях работают с четырёхбайтовыми знаковыми величинами, и работа около границ диапазонов представления происходит редко. В-третьих, арифметические операции с целыми – достаточно частый код (в отличие от операций с массивами), и добавление дополнительной работы на каждую операцию иногда может быть заметно (в смысле производительности).
  • «I/O Checking» – эта опция используется только при работе с файлами в стиле Паскаля, которые считаются устаревшими. По-хорошему, вы не должны использовать их и, соответственно, эту опцию.

Замечу также, что эти опции можно выставлять и локально – как для целого модуля, так и для отдельной функции/процедуры (а для некоторых опций – даже для участка кода). Делается это обычными директивами компилятора, узнать которые вы можете, нажав F1 в окне настроек. Например, «Stack Frames» регулируется {$W+} и {$W-}.

Кроме того, помимо настроек компилятора (Compiling) есть ещё настройки компоновщика (Linking):

  • «Map file» – включение опции заставляет линкёр Delphi создавать вместе с проектом map-файл. Различные установки опции отвечают за уровень детализации и обычно имеет смысл ставить только Off или Detailed. Map файл обычно используется всевозможными утилитами типа EurekaLog, JCL или madExcept в качестве первичного источника для создания отладочной информации в своём формате. Поэтому руками устанавливать эту опцию вам придётся крайне редко – эксперты включают её самостоятельно по необходимости.
  • «Debug Information» (Linker)/«Include TD32 debug info» – внедряет в приложение отладочную информацию для внешнего отладчика в формате TD32. Обычно эта опция включается, если вы отлаживаете проект через Attach to process и Delphi не может найти отладочную информацию. При включении этой опции размер самого приложения увеличивается в 5-10 раз (при условии, что опция «Place debug information in separate TDS file» выключена). Поэтому, если вам нужна отладочная информация в распространяемом приложении – лучше рассмотреть другие варианты (лучше всего подходит отладочная информация в специализированных форматах – EurekaLog, JCL, madExcept).
  • «Include remote debug symbols” – заставляет линкёр создать rsm-файл вместе с проектом, в который записывается информация для удалённого отладчика Delphi. Вам нужно включать эту опцию, если вы хотите выполнить удалённую отладку. Полученный rsm-файлик нужно копировать вместе с приложением на удалённую машину.

Всю эту информацию можно просуммировать и дать рекомендации по установке опций для различных случаев жизни. Они приведены ниже. Жирным выделены настройки, отличающиеся от умалчиваемых (т.е. на них нужно обратить внимание и выставить вручную).

Хочу заметить, что помимо опций Delphi, в самих экспертах/инструментах также могут быть настройки, влияющие на детальность отладочной информации. Например, обзор таких настроек для EurekaLog мы уже делали в этой статье.

Обычное приложение без механизма диагностики исключений

Общие настройки для любых профилей

Все опции отладки («Debug information» (Compiler), «Local symbols», «Reference info») вообще на готовый модуль не влияют, т.к. отладочная информация в exe/DLL не хранится, ну и нам жить не мешают => поэтому смысла их выключать я не вижу («Use Debug DCUs» – устанавливайте по вкусу, смотря по тому, хотите вы отлаживаться в стандартных модулях или нет).

«Stack Frames» вообще включать незачем.

Генерацию map-файла выключаем.

Профиль Debug

Включаем «Range checking» и (по вкусу) «Overflow checking».

«Include TD32 debug info» – включаем только для отладки внешнего процесса.

Соответственно, «Include remote debug info» – включаем только для удалённой отладки.

Профиль Release

Выключаем «Range checking», «Overflow checking», «Include TD32 debug info» и «Include remote debug info».

Приложение с механизмом диагностики исключений (типа EurekaLog, JCL или madExcept)

Общие настройки любых профилей

Все опции отладки («Debug information» (Compiler), «Local symbols», «Reference info») держать включёнными, т.к. в противном случае не будет доступна отладочная информация. Соответственно, ваши информационные механизмы пойдут лесом.

«Stack Frames» – вообще вЫключать незачем.

Генерацию map-файла включаем, если об этом не позаботился эксперт (маловероятно).

Профиль Debug

«Use Debug DCUs» – по вкусу.
Включаем «Range checking» и (по вкусу) «Overflow checking».

«Include TD32 debug info» – включаем только для отладки внешнего процесса.

«Include remote debug info» – включаем только для удалённой отладки.

Профиль Release

Включаем «Use Debug DCUs».

Выключаем «Range checking», «Overflow checking», «Include TD32 debug info» и «Include remote debug info».

Примечание: если вы используете мало операций с индексами в своей программе (так что дополнительные проверки не замедлят её), то будет хорошей идеей всегда держать опцию «Range checking» включённой.

Debugging tips from Delphi 2009 Live!

Фото с Delphi 2009 Live!

Что может пойти не так, если настройки будут заданы неверно?

Ну, во-первых, это невозможность отладки (например, отсутствие информации для удалённого отладчика или выключенная опция «Debug information» (Compiler)), большой размер приложения (например, случайно забыли выключить «Debug information» (Linker)/«Include TD32 debug info»), медленная работа (например, компиляция с отладочным кодом), отсутствие или неполный стек вызовов в средствах диагностики исключений (например, выключили «Debug information» (Compiler)). В очень редких и запущенных случаях переключение опций может сказаться на работоспособности программы (например, установка Stack frames может снизить максимально возможную глубину рекурсии). Ну и недочёты по мелочи.

Кстати, если вы разрабатываете компоненты, то надо учитывать, что у Delphi есть именно два набора DCU-файлов, которые отличаются настройками компиляции. Вообще говоря, простое переключение опций, ответственных за генерацию отладочной информации, никак не влияет на интерфейс и реализацию модуля. Но в общем случае код может использовать условные директивы. Поэтому может быть ситуация, когда два набора DCU файлов (скомпилированных с разными опциями) не совместимы друг с другом – потому что они использовали какую-либо директиву и содержат разный код (а вот и пример).
Поэтому вам тоже надо иметь два набора DCU-файлов: один – скомпилированный с опцией «Use Debug DCUs», другой – без. Причём, не важно включена или выключена опция «Debug information» (Compiler) в ваших настройках в обоих случаях.

Примечания:
(*) Слова “отладочная информация увеличивает время компиляции, размер dcu-файлов, но не влияет на размер и скорость работы полученного exe-файла” некоторые понимают неправильно. В частности, многие замечают, что если переключить профиль приложения с Debug на Release (или наоборот), то размер приложения изменится (незначительно или же намного – зависит от настроек проекта и его исходного кода). Позвольте, но ведь я говорю об отладочной информации в чистом виде (мы говорим про опции “Debug Information”, “Local Symbols” и т.п. с вкладки “Compiling”), а вы меняете профиль компиляции целиком. Это не только смена опций отладочной информации (которые, кстати, могут даже вообще не меняться при смене профиля), а также и множество других настроек.

Например, в вашем коде может быть тьма конструкций вида {$IFDEF DEBUG}какой-то код{$ENDIF}. Разумеется, когда вы собираете программу в Release, этот код в программу не попадёт, а когда вы собираете его в Debug, то – попадёт. Потому что по умолчанию профиль Debug содержит символ условной компиляции “DEBUG”. В результате размер .exe действительно получится разный. Но повлияла ли на этот размер отладочная информация? Неа. Фактически, вы собрали в разных профилях разных код – неудивительно, что он разный по размеру.

Более того, вы можете удалить из конфигурации Debug символ условной компиляции “DEBUG” – и тогда вы будете собирать один и тот же код в обоих профилях. Ну, по крайней мере, свой код. Сторонний, уже собранный код, конечно же, никак не будет затронут. Например, код RTL/VCL уже скомпилирован с фиксированными настройками и не меняется при пересборке проекта. Вон там чуть выше я упомянул, что в некоторых версиях Delphi отладочный и релизный варианты кода RTL/VCL незначительно отличаются – опять же, благодаря условной компиляции. Но никак не отладочной информации.

Кроме того, к иному коду приводит и включение/выключение опций вида Optimization, Stack Frames, Range Check Errors и др. Действительно, оптимизация может удалять код (оптимизировать ненужный) или увеличивать (вставлять inline вместо вызова), Stack Frames очевидно добавляет код (код установки фрейма), равно как и Range Check Errors (код проверки диапазонов). В результате, вы меняете профиль – вы меняете и код (при условии, что разные профили имеют разные настройки вышеупомянутых опций – что так и есть по умолчанию). Меняете код – меняете и размер. Имеет ли хоть какое-то отношение к этому изменению размера отладочная информация? Нет.

Далее, другой пример. Несмотря на то, что по умолчанию отладочная информация в .exe файл не попадает – никто же не запрещает её вам туда добавить самому! Например, если вы будете использовать трейсер исключений, то он самостоятельно добавит в .exe часть отладочной информации, которая нужна ему для построения читабельных стеков вызовов.

Также о внедрении отладочной информации можно попросить и среду. Например, вы можете включить “Include TD32 Debug Info” (эта опция называется “Debug Information” на вкладке “Linking” в последних версиях Delphi) и выключить опцию “Place debug information in separate TDS file”. Тогда вся отладочная информация из .dcu файлов будет собрана в один большой файл и этот файл будет внедрён в .exe. Тогда да, отладочная информация повлияет на размер файла, но это происходит не из-за её свойств, а потому что мы явно попросили такое поведение: “добавьте отладочную инфу в мою программу”.

Кстати говоря, в последних версиях Delphi здорово поменяли настройки профилей компиляции. Если раньше Release и Debug мало чем отличались, то теперь отличия существенны. Профиль Debug выключает Optimization, но включает Stack Frames (а профиль Release – делает наоборот). Профиль Debug включает отладочную информацию, а Release – отключает. Но оба профиля включают Assert-ы. Также оба профиля включают I/O checks, но выключают overflow и range. В настройках компоновщика (linking) профиль Debug включает TD32 (“Debug information”) и Remote debug symbols (не для всех платформ). Release, соответственно, выключает эти же опции. И оба профиля не включают Map file и отдельный файл для TD32.

(**) Например:

procedure TForm1.Button1Click(Sender: TObject);

  procedure A;

    procedure B;
    begin // синяя точка слева от "begin" =>
          // эта функция имеет стековый фрейм
      ShowMessage(IntToStr(Integer(nil^)));
    end;

  begin // нет синей точки слева от "begin" =>
        // это очень короткая процедура, поэтому
        // стековый фрейм не нужен =>
        // он не создаётся.
    B;
  end;

begin // нет синей точки слева от "begin" =>
    // это очень короткая процедура, поэтому
    // стековый фрейм не нужен =>
    // он не создаётся.
  A;
end;

Button1Click состоит всего из двух инструкций: “call A; ret;”. Она очень короткая и не использует аргументы или локальные переменные. Поэтому, очевидно, что ей не нужен стековый фрейм. Когда опция “Stack frames” выключена, то для Button1Click стековый фрейм не создаётся (но он создаётся, если опция “Stack frames” будет включена).

Но, для более сложных процедур стековые фреймы будут генерироваться вне зависимости от установки опции “Stack frames”.

Например, тоже очень короткая процедура B всегда имеет фрейм. Причина: использование типа String в ShowMessage. Компилятору нужно вставить неявную строковую переменную и неявный try/finally для её освобождения, поэтому процедуре нужен фрейм.

В реальных приложениях фреймы генерируются для 99% процедур. Подробнее: Фреймы на стеке.

Добавить комментарий