青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

大熊的口袋

 

[轉載]Stack frame omission (FPO) optimization and consequences when debugging

Part1:

During the course of debugging programs, you’ve probably ran into the term “FPO” once or twice. FPO refers to a specific class of compiler optimizations that, on x86, deal with how the compiler accesses local variables and stack-based arguments.

With a function that uses local variables (and/or stack-based arguments), the compiler needs a mechanism to reference these values on the stack. Typically, this is done in one of two ways:
Access local variables directly from the stack pointer (esp). This is the behavior if FPO optimization is enabled. While this does not require a separate register to track the location of locals and arguments, as is needed if FPO optimization is disabled, it makes the generated code slightly more complicated. In particular, the displacement from esp of locals and arguments actually changes as the function is executed, due to things like function calls or other instructions that modify the stack. As a result, the compiler must keep track of the actual displacement from the current esp value at each location in a function where a stack-based value is referenced. This is typically not a big deal for a compiler to do, but in hand written assembler, this can get a bit tricky.
Dedicate a register to point to a fixed location on the stack relative to local variables and and stack-based arguments, and use this register to access locals and arguments. This is the behavior if FPO optimization is disabled. The convention is to use the ebp register to access locals and stack arguments. Ebp is typically setup such that the first stack argument can be found at [ebp+08], with local variables typically at a negative displacement from ebp.

A typical prologue for a function with FPO optimization disabled might look like this:
push   ebp               ; save away old ebp (nonvolatile)
mov    ebp, esp          ; load ebp with the stack pointer
sub    esp, sizeoflocals ; reserve space for locals
...                      ; rest of function

The main concept is that FPO optimization is disabled, a function will immediately save away ebp (as the first operation touching the stack), and then load ebp with the current stack pointer. This sets up a stack layout like so (relative to ebp):
[ebp-01]   Last byte of the last local variable
[ebp+00]   Old ebp value
[ebp+04]   Return address
[ebp+08]   First argument...

Thereafter, the function will always use ebp to access locals and stack based arguments. (The prologue of the function may vary a bit, especially with functions using a variation __SEH_prolog to setup an initial SEH frame, but the end result is always the same with respect to the stack layout relative to ebp.)

This does (as previously stated) make it so that the ebp register is not available for other uses to the register allocator. However, this performance hit is usually not enough to be a large concern relative to a function compiled with FPO optimization turned on. Furthermore, there are a number of conditions that require a function to use a frame pointer which you may hit anyway:
Any function using SEH must use a frame pointer, as when an exception occurs, there is no way to know the displacement of local variables from the esp value (stack pointer) at exception dispatching (the exception could have happened anywhere, and operations like making function calls or setting up stack arguments for a function call modify the value of esp).
Any function using automatic C++ objects with destructors must use SEH for compiler unwind support. This means that most C++ functions end up with FPO optimization disabled. (It is possible to change the compiler assumptions about SEH exceptions and C++ unwinding, but the default [and recommended setting] is to unwind objects when an SEH exception occurs.)
Any function using _alloca to dynamically allocate memory on the stack must use a frame pointer (and thus have FPO optimization disabled), as the displacement from esp for local variables and arguments can change at runtime and is not known to the compiler at compile time when code is being generated.

Because of these restrictions, many functions you may be writing will already have FPO optimization disabled, without you having explicitly turned it off. However, it is still likely that many of your functions that do not meet the above criteria have FPO optimization enabled, and thus do not use ebp to reference locals and stack arguments.

Now that you have a general idea of just what FPO optimization does, I’ll cover cover why it is to your advantage to turn off FPO optimization globally when debugging certain classes of problems in the second half of this series. (It is actually the case that most shipping Microsoft system code turns off FPO as well, so you can rest assured that a real cost benefit analysis has been done between FPO and non-FPO optimized code, and it is overall better to disable FPO optimization in the general case.)

Update: Pavel Lebedinsky points out that the C++ support for SEH exceptions is disabled by default for new projects in VS2005 (and that it is no longer the recommended setting). For most programs built prior to VS2005 and using the defaults at that time, though, the above statement about C++ destructors causing SEH to be used for a function (and thus requiring the use of a frame pointer) still applies.



Part2:
Last time, I outlined the basics as to just what FPO does, and what it means in terms of generated code when you compile programs with or without FPO enabled. This article builds on the last, and lays out just what the impacts of having FPO enabled (or disabled) are when you end up having to debug a program.

For the purposes of this article, consider the following example program with several do-nothing functions that shuffle stack arguments around and call eachother. (For the purposes of this posting, I have disabled global optimizations and function inlining.)
__declspec(noinline)
void
f3(
   int* c,
   char* b,
   int a
   )
{
   *c = a * 3 + (int)strlen(b);

   __debugbreak();
}

__declspec(noinline)
int
f2(
   char* b,
   int a
   )
{
   int c;

   f3(
      &c,
      b + 1,
      a - 3);

   return c;
}

__declspec(noinline)
int
f1(
   int a,
   char* b
   )
{
   int c;

   c = f2(
      b,
      a + 10);

   c ^= (int)rand();

   return c + 2 * a;
}

int
__cdecl
wmain(
   int ac,
   wchar_t** av
   )
{
   int c;

   c = f1(
      (int)rand(),
      "test");

   printf("%d\n",
      c);

   return 0;
}

If we run the program and break in to the debugger at the hardcoded breakpoint, with symbols loaded, everything is as one might expect:
0:000> k
ChildEBP RetAddr
0012ff3c 010015ef TestApp!f3+0x19
0012ff4c 010015fe TestApp!f2+0x15
0012ff54 0100161b TestApp!f1+0x9
0012ff5c 01001896 TestApp!wmain+0xe
0012ffa0 77573833 TestApp!__tmainCRTStartup+0x10f
0012ffac 7740a9bd kernel32!BaseThreadInitThunk+0xe
0012ffec 00000000 ntdll!_RtlUserThreadStart+0x23

Regardless of whether FPO optimization is turned on or off, since we have symbols loaded, we’ll get a reasonable call stack either way. The story is different, however, if we do not have symbols loaded. Looking at the same program, with FPO optimizations enabled and symbols not loaded, we get somewhat of a mess if we ask for a call stack:
0:000> k
ChildEBP RetAddr
WARNING: Stack unwind information not available.
Following frames may be wrong.
0012ff4c 010015fe TestApp+0x15d8
0012ffa0 77573833 TestApp+0x15fe
0012ffac 7740a9bd kernel32!BaseThreadInitThunk+0xe
0012ffec 00000000 ntdll!_RtlUserThreadStart+0x23

Comparing the two call stacks, we lost three of the call frames entirely in the output. The only reason we got anything slightly reasonable at all is that WinDbg’s stack trace mechanism has some intelligent heuristics to guess the location of call frames in a stack where frame pointers are used.

If we look back to how call stacks are setup with frame pointers (from the previous article), the way a program trying to walk the stack on x86 without symbols works is by treating the stack as a sort of linked list of call frames. Recall that I mentioned the layout of the stack when a frame pointer is used:
[ebp-01]   Last byte of the last local variable
[ebp+00]   Old ebp value
[ebp+04]   Return address
[ebp+08]   First argument...

This means that if we are trying to perform a stack walk without symbols, the way to go is to assume that ebp points to a “structure” that looks something like this:
typedef struct _CALL_FRAME
{
   struct _CALL_FRAME* Next;
   void*               ReturnAddress;
} CALL_FRAME, * PCALL_FRAME;

Note how this corresponds to the stack layout relative to ebp that I described above.

A very simple stack walk function designed to walk frames that are compiled with frame pointer usage might then look like so (using the _AddressOfReturnAddress intrinsic to find “ebp”, assuming that the old ebp is 4 bytes before the address of the return address):
LONG
StackwalkExceptionHandler(
   PEXCEPTION_POINTERS ExceptionPointers
   )
{
   if (ExceptionPointers->ExceptionRecord->ExceptionCode
      == EXCEPTION_ACCESS_VIOLATION)
      return EXCEPTION_EXECUTE_HANDLER;

   return EXCEPTION_CONTINUE_SEARCH;
}

void
stackwalk(
   void* ebp
   )
{
   PCALL_FRAME frame = (PCALL_FRAME)ebp;

   printf("Trying ebp %p\n",
      ebp);

   __try
   {
      for (unsigned i = 0;
          i < 100;
          i++)
      {
         if ((ULONG_PTR)frame & 0x3)
         {
            printf("Misaligned frame\n");
            break;
         }

         printf("#%02lu %p  [@ %p]\n",
            i,
            frame,
            frame->ReturnAddress);

         frame = frame->Next;
      }
   }
   __except(StackwalkExceptionHandler(
      GetExceptionInformation()))
   {
      printf("Caught exception\n");
   }
}

#pragma optimize("y", off)
__declspec(noinline)
void printstack(
   )
{
   void* ebp = (ULONG*)_AddressOfReturnAddress()
     - 1;

   stackwalk(
      ebp);
}
#pragma optimize("", on)

If we recompile the program, disable FPO optimizations, and insert a call to printstack inside the f3 function, the console output is something like so:
Trying ebp 0012FEB0
#00 0012FEB0  [@ 0100185C]
#01 0012FED0  [@ 010018B4]
#02 0012FEF8  [@ 0100190B]
#03 0012FF2C  [@ 01001965]
#04 0012FF5C  [@ 01001E5D]
#05 0012FFA0  [@ 77573833]
#06 0012FFAC  [@ 7740A9BD]
#07 0012FFEC  [@ 00000000]
Caught exception

In other words, without using any symbols, we have successfully performed a stack walk on x86.

However, this all breaks down when a function somewhere in the call stack does not use a frame pointer (i.e. was compiled with FPO optimizations enabled). In this case, the assumption that ebp always points to a CALL_FRAME structure is no longer valid, and the call stack is either cut short or is completely wrong (especially if the function in question repurposed ebp for some other use besides as a frame pointer). Although it is possible to use heuristics to try and guess what is really a call/return address record on the structure, this is really nothing more than an educated guess, and tends to be at least slightly wrong (and typically missing one or more frames entirely).

Now, you might be wondering why you might care about doing stack walk operations without symbols. After all, you have symbols for the Microsoft binaries that your program will be calling (such as kernel32) available from the Microsoft symbol server, and you (presumably) have private symbols corresponding to your own program for use when you are debugging a problem.

Well, the answer to that is that you will end up needing to record stack traces without symbols in the course of normal debugging for a wide variety of problems. The reason for this is that there is a lot of support baked into NTDLL (and NTOSKRNL) to assist in debugging a class of particularly insidious problems: handle leaks (and other problems where the wrong handle value is getting closed somewhere and you need to find out why), memory leaks, and heap corruption.

These (very useful!) debugging features offer options that allow you to configure the system to log a stack trace on each heap allocation, heap free, or each time a handle is opened or closed. Now the way these features work is that they will capture the stack trace in real time as the heap operation or handle operation happens, but instead of trying to break into the debugger to display the results of this output (which is undesirable for a number of reasons), they save a copy of the current stack trace in-memory and then continue execution normally. To display these saved stack traces, the !htrace, !heap -p, and !avrf commands have functionality that locates these saved traces in-memory and prints them out to the debugger for you to inspect.

However, NTDLL/NTOSKRNL needs a way to create these stack traces in the first place, so that it can save them for later inspection. There are a couple of requirements here:
The functionality to capture stack traces must not rely on anything layed above NTDLL or NTOSKRNL. This already means that anything as complicated as downloading and loading symbols via DbgHelp is instantly out of the picture, as those functions are layered far above NTDLL / NTOSKRNL (and indeed, they must make calls into the same functions that would be logging stack traces in the first place in order to find symbols).
The functionality must work when symbols for everything on the call stack are not even available to the local machine. For instance, these pieces of functionality must be deployable on a customer computer without giving that computer access to your private symbols in some fashion. As a result, even if there was a good way to locate symbols where the stack trace is being captured (which there isn’t), you couldn’t even find the symbols if you wanted to.
The functionality must work in kernel mode (for saving handle traces), as handle tracing is partially managed by the kernel itself and not just NTDLL.
The functionality must use a minimum amount of memory to store each stack trace, as operations like heap allocation, heap deallocation, handle creation, and handle closure are extremely frequent operations throughout the lifetime of the process. As a result, options like just saving the entire thread stack for later inspection when symbols are available cannot be used, since that would be prohibitively expensive in terms of memory usage for each saved stack trace.

Given all of these restrictions, the code responsible for saving stack traces needs to operate without symbols, and it must furthermore be able to save stack traces in a very concise manner (without using a great deal of memory for each trace).

As a result, on x86, the stack trace saving code in NTDLL and NTOSKRNL assumes that all functions in the call frame use frame pointers. This is the only realistic option for saving stack traces on x86 without symbols, as there is insufficient information baked into each individual compiled binary to reliably perform stack traces without assuming the use of a frame pointer at each call site. (The 64-bit platforms that Windows supports solve this problem with the use of extensive unwind metadata, as I have covered in a number of past articles.)

So, the functionality exposed by pageheap’s stack trace logging, and handle tracing are how stack traces without symbols end up mattering to you, the developer with symbols for all of your binaries, when you are trying to debug a problem. If you make sure to disable FPO optimization on all of your code, then you’ll be able to use tools like pageheap’s stack tracing on heap operations, UMDH (the user mode heap debugger), and handle tracing to track down heap-related problems and handle-related problems. The best part of these features is that you can even deploy them on a customer site without having to install a full debugger (or run your program under a debugger), only later taking a minidump of your process for examination in the lab. All of them rely on FPO optimizations being disabled (at least on x86), though, so remember to turn FPO optimizations off on your release builds for the increased debuggability of these tough-to-find problems in the field.

posted on 2009-10-29 18:01 大熊的口袋 閱讀(840) 評論(0)  編輯 收藏 引用 所屬分類: cpp

導航

統計

公告

常用鏈接

留言簿(2)

隨筆分類

隨筆檔案

win32 & debug

搜索

積分與排名

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美日韩亚洲高清一区二区| 99视频在线精品国自产拍免费观看| 国产精品揄拍一区二区| 久久精品亚洲精品国产欧美kt∨| 六月婷婷久久| 久久gogo国模裸体人体| 美腿丝袜亚洲色图| 久久久99国产精品免费| 欧美日在线观看| 亚洲精品视频免费观看| 亚洲大胆美女视频| 欧美一区二区播放| 欧美一区二区三区电影在线观看| 欧美大胆成人| 欧美激情一区二区| 亚洲福利视频免费观看| 亚洲精品中文字幕有码专区| 久久亚洲欧美| 欧美高清视频在线| 亚洲欧洲在线免费| 蜜臀av一级做a爰片久久| 亚洲第一精品福利| 免费久久99精品国产自| 一区二区三区在线观看国产| 老牛嫩草一区二区三区日本| 欧美大片免费久久精品三p | 一区二区91| 欧美视频中文在线看| 午夜视频一区二区| 久久婷婷久久一区二区三区| 亚洲人成精品久久久久| 欧美私人啪啪vps| 久久精品国产99精品国产亚洲性色| 狂野欧美激情性xxxx欧美| 亚洲免费成人av| 国产精品视频yy9099| 久久视频免费观看| 午夜免费日韩视频| 亚洲美女电影在线| 亚洲国产小视频| 久久久噜噜噜久久中文字幕色伊伊 | 日韩午夜免费| 国产日本亚洲高清| 欧美日韩在线视频首页| 久久精品国产一区二区电影| 日韩一级精品| 亚洲另类自拍| 亚洲全部视频| 亚洲日本电影在线| 亚洲第一福利视频| 欧美高潮视频| 亚洲国产精品99久久久久久久久| 久久久噜久噜久久综合| 裸体女人亚洲精品一区| 久久久久久久久久久久久女国产乱 | 日韩视频精品| 日韩亚洲欧美成人一区| 日韩视频在线观看国产| 亚洲区中文字幕| 亚洲国产成人在线播放| 亚洲电影免费在线观看| 日韩视频欧美视频| 在线亚洲自拍| 欧美伊人影院| 老司机精品视频一区二区三区| 老司机久久99久久精品播放免费| 久久久青草婷婷精品综合日韩| 久久躁日日躁aaaaxxxx| 欧美精品一区二区三区一线天视频| 欧美日精品一区视频| 国产主播一区二区| avtt综合网| 新狼窝色av性久久久久久| 亚洲精品美女久久久久| 一本色道久久综合亚洲精品小说| 中日韩高清电影网| 另类成人小视频在线| 国产精品日韩欧美大师| 亚洲福利视频一区| 西西裸体人体做爰大胆久久久| 免费成人激情视频| 国产在线欧美日韩| 亚洲欧美中日韩| 亚洲福利视频网| 久久精品视频播放| 好看的亚洲午夜视频在线| 午夜精品亚洲一区二区三区嫩草| 亚洲国产女人aaa毛片在线| 久久久蜜桃精品| 亚洲高清视频在线| 久热精品在线视频| 久久久久天天天天| 国外成人免费视频| 欧美高清在线观看| 欧美不卡视频| 亚洲深夜福利| 亚洲女优在线| 在线观看不卡| 亚洲国产精品成人一区二区 | 欧美激情综合色综合啪啪| 在线观看91精品国产麻豆| 亚洲大胆av| 欧美黑人在线播放| 欧美xart系列高清| 亚洲一区二区久久| 久久狠狠久久综合桃花| 1000部国产精品成人观看| 亚洲韩国青草视频| 国产日韩在线看| 亚洲欧洲日产国产综合网| 国产精品h在线观看| 麻豆成人综合网| 欧美日韩精品在线| 玖玖国产精品视频| 欧美视频在线不卡| 亚洲国产欧美日韩精品| 国产女人18毛片水18精品| 日韩午夜电影av| 在线观看欧美| 亚洲午夜av电影| 亚洲欧美成aⅴ人在线观看| 久久视频一区| 久久午夜电影网| 国产精品久久久久久久久久久久久久| 久久综合99re88久久爱| 国产亚洲午夜| 欧美在线三级| 久久不见久久见免费视频1| 国产精品国产三级国产aⅴ无密码| 亚洲美女在线视频| 亚洲免费精彩视频| 欧美日韩精品免费观看视一区二区| 久久国产精品久久久久久电车| 国产精品卡一卡二| 亚洲尤物影院| 免播放器亚洲| 一本到高清视频免费精品| 99精品国产热久久91蜜凸| 欧美激情久久久久久| 国产一区二区三区久久| 亚洲小少妇裸体bbw| 久久综合久久久久88| 在线成人激情视频| 欧美日韩亚洲另类| 欧美一区二区三区四区在线观看| 欧美一区二区三区视频| 国产综合18久久久久久| 久久午夜av| 亚洲视频视频在线| 欧美风情在线观看| 欧美一级视频精品观看| 亚洲人成在线观看一区二区| 欧美网站在线观看| 欧美电影免费观看高清| 欧美一区1区三区3区公司| 欧美一级久久久| 日韩视频免费| 欧美韩日一区| 欧美jjzz| 欧美v国产在线一区二区三区| 亚洲欧美精品中文字幕在线| 亚洲精选一区二区| 国产综合色在线| 国产亚洲美州欧州综合国| 国产精品盗摄久久久| 欧美精品97| 欧美极品在线视频| 欧美日韩国产高清| 欧美日韩一区二区高清| 欧美精品v日韩精品v韩国精品v | 亚洲一区二区影院| 亚洲毛片在线| 中文日韩电影网站| 亚洲欧美日韩精品| 亚洲一区www| 午夜精品一区二区在线观看| 亚洲欧美精品一区| 久久精品视频在线| 免费在线一区二区| 亚洲国产欧美不卡在线观看 | 国产精品视频网| 国模吧视频一区| 亚洲精品少妇30p| 亚洲少妇一区| 欧美v国产在线一区二区三区| 亚洲黄色成人网| 欧美在线一级视频| 欧美成人dvd在线视频| 国产精品乱码一区二区三区| 伊人精品久久久久7777| 亚洲一区3d动漫同人无遮挡| 久久九九精品99国产精品| 亚洲精品乱码久久久久久久久| 性感少妇一区| 欧美日韩在线第一页| 91久久香蕉国产日韩欧美9色 | 欧美午夜精品久久久久免费视| 国产乱子伦一区二区三区国色天香| 亚洲国内高清视频| 男人插女人欧美|