代码之家  ›  专栏  ›  技术社区  ›  Peter Hosey

程序通过纯粹的意志力溢出调用堆栈

  •  2
  • Peter Hosey  · 技术社区  · 14 年前

    这是来自 a user's crash report :

    Thread 0 Crashed:  Dispatch queue: com.apple.main-thread
    0   com.growl.GrowlSafari           0x179d383c writeWithFormat + 25
    1   com.growl.GrowlSafari           0x179d388e writeWithFormat + 107
    2   com.growl.GrowlSafari           0x179d388e writeWithFormat + 107
    3   com.growl.GrowlSafari           0x179d388e writeWithFormat + 107
    4   com.growl.GrowlSafari           0x179d388e writeWithFormat + 107
    5   com.growl.GrowlSafari           0x179d388e writeWithFormat + 107
    

    痕迹在511帧处被切断。

    这里是 writeWithFormat :

    int writeWithFormat(FILE *file, NSString *format, ...) {
        va_list args;
        va_start(args, format);
        int written = writeWithFormatAndArgs(file, format, args);
        va_end(args);
        return written;
    }
    

    正如你所看到的,它并不称自己为。

    它调用的函数如下:

    int writeWithFormatAndArgs(FILE *file, NSString *format, va_list args) {
        return 0;
        return fprintf(file, "%s\n", [[[[NSString alloc] initWithFormat:format arguments:args] autorelease] UTF8String]);
    }
    

    (您可以猜到,这是未激活的日志代码。)

    那么,这段代码是如何产生堆栈跟踪的呢?


    使用OTX拆卸:

    _writeWithFormatAndArgs:
        +0  00000f68  55                    pushl       %ebp
        +1  00000f69  89e5                  movl        %esp,%ebp
        +3  00000f6b  31c0                  xorl        %eax,%eax
        +5  00000f6d  c9                    leave
        +6  00000f6e  c3                    ret
    
    _writeWithFormat:
        +0  00001823  55                    pushl       %ebp
        +1  00001824  89e5                  movl        %esp,%ebp
        +3  00001826  83ec10                subl        $0x10,%esp
        +6  00001829  31c0                  xorl        %eax,%eax
        +8  0000182b  c9                    leave
        +9  0000182c  c3                    ret
    
    2 回复  |  直到 14 年前
        1
  •  3
  •   Peter Hosey    14 年前

    在完整的崩溃报告中,您可以在“二进制文件”部分中看到用户加载了两个growsfari副本:

     0x140c000 -  0x140efff +com.growl.GrowlSafari 1.1.6 (1.1.6) <1E774BDF-5CC5-4876-7C66-380EBFEAF190> /Library/InputManagers/GrowlSafari/GrowlSafariLoader.bundle/Contents/PlugIns/GrowlSafari.bundle/Contents/MacOS/GrowlSafari
    0x179d2000 - 0x179d4ff7 +com.growl.GrowlSafari 1.2.1 (1.2.1) <10F1EF69-D655-CCEE-DF3A-1F6C0CF541D3> /Applications/GrowlSafari.app/Contents/Resources/GrowlSafari.bundle/Contents/MacOS/GrowlSafari
    

    我在问题中展示的代码来自1.2.1(但很可能从1.1.6开始它就没有改变),而反汇编代码是1.2.1。

    这可能是导致问题的原因,特别是当递归看起来实际上是在一个Swizzled方法中时(感谢Twitter上的@karsten pointing this out )

        2
  •  1
  •   bbum    14 年前

    其中某个地方的尾部调用可能会导致一个或多个函数从堆栈跟踪中消失,这当然会使调试变得很有趣。

    从我的头脑中,我可以想到两种可能导致这种情况的情景:

    • 格式字符串是nsstring的子类,或者某个类别导致在该writeWithFormat()期间调用WriteWithFormat()。自定义日志记录代码有时也会这样做——编写一些通用的自定义日志记录代码非常容易,这些代码会很高兴地回调到自身中。去了,做了。很多次,可悲。

    • 内存的反常导致了递归;损坏的对象或其他东西。

    两人都有点抓狂。发布整个崩溃报告。


    两个版本的包在运行时旋转…所有的赌注都被取消了,直到撞车事件在只有一个捆绑包的情况下重现。我敢打赌这就是问题所在。

    我还打赌有一些相关的控制台spew。