3

我正在尝试创建自己的自定义断言。但是,我希望我的断言自动包含所有相关变量。这对我来说似乎真的很基本,我已经搜索了大约一个小时,但我似乎找不到访问所有相关堆栈帧变量的方法。有谁知道如何获得这些变量?

仅供参考 - 我不需要访问调试器中的变量,我需要以编程方式访问它们。我想将它们与崩溃报告一起上传,以提供有关崩溃的更多信息。我也知道我可以手动打印出来……这正是我想要避免的。

4

3 回答 3

6

您基本上是在要求重新发明一大块调试器。

没有符号,就没有任何东西可以询问来确定本地框架的布局。即使使用符号,优化器也很可能会踩到任何局部变量,因为一旦确定帧内不再需要变量,优化器就会随心所欲地重新使用堆栈槽。

请注意,许多崩溃根本无法被捕获,或者,如果被捕获,它们发生的框架早就被破坏了。

既然您提到您正在创建一个自定义断言,那么当您以编程方式检测到事情已经偏离轨道时,听起来您真的不想像转储本地框架的快照那样自省崩溃。虽然确实没有自动报告本地堆栈状态的方法,但您可以执行以下操作:

{ ... some function ....
  ... local variables ...
  #define reportblock ^{ ... code that summarizes locals ... ; return summary; }

  YourAssert( cond, "cond gone bad. summary: %@", reportblock());
}

请注意,#define 确保每个 YourAssert() 捕获断言时的状态。另请注意,上述内容可能会对性能产生潜在的重大影响。

另请注意,我刚刚编写了该代码。看起来它值得调查,但由于多种原因可能证明不可行。

于 2013-05-09T22:18:05.563 回答
2

我决定将其添加为单独的答案,因为它使用与我的另一个相同的方法,但这次使用的是所有 ObjC 代码。不幸的是,您仍然必须像以前一样重新声明所有堆栈变量,但希望现在它可以更好地与您现有的代码库一起使用。

堆栈变量.h

#import <Foundation/Foundation.h>

#define LOCAL_VAR(p_type, p_name, p_value)\
    p_type p_name = p_value;\
    StackVariable *__stack_ ## p_name = [[StackVariable alloc] initWithPointer:&p_name\
                                                                        size:sizeof(p_type)\
                                                                        name:#p_name\
                                                                        type:#p_type\
                                                                        file:__FILE__\
                                                                        line:__LINE__];\
    (void) __stack_ ## p_name;

@interface StackVariable : NSObject

-(id) initWithPointer:(void *) ptr
                 size:(size_t) size
                 name:(const char *) name
                 type:(const char *) type
                 file:(const char *) file
                 line:(const int) line;

+(NSString *) dump;

@end

堆栈变量.m:

#import "StackVariable.h"

static NSMutableArray *stackVariables;

@implementation StackVariable {
    void *_ptr;
    size_t _size;
    const char *_name;
    const char *_type;
    const char *_file;
    int _line;
}

-(id) initWithPointer:(void *)ptr size:(size_t)size name:(const char *)name type:(const char *)type file:(const char *)file line:(int)line
{
    if (self = [super init]) {
        if (stackVariables == nil) {
            stackVariables = [NSMutableArray new];
        }

        _ptr = ptr;
        _size = size;
        _name = name;
        _type = type;
        _file = file;
        _line = line;

        [stackVariables addObject:[NSValue valueWithNonretainedObject:self]];
    }

    return self;
}

-(NSString *) description {
    NSMutableString *result = [NSMutableString stringWithFormat:@"%s:%d - %s %s = { ", _file, _line, _type, _name];

    const uint8_t *bytes = (const uint8 *) _ptr;

    for (size_t i = 0; i < _size; i++) {
        [result appendFormat:@"%02x ", bytes[i]];
    }

    [result appendString:@"}"];

    return result;
}

+(NSString *) dump {
    NSMutableString *result = [NSMutableString new];

    for (NSValue *value in stackVariables) {
        __weak StackVariable *var = [value nonretainedObjectValue];

        [result appendString:[var description]];
        [result appendString:@"\n"];
    }

    return result;
}

-(void) dealloc {
    [stackVariables removeObject:[NSValue valueWithNonretainedObject:self]];
}

@end

例子:

#include "StackVariable.h"

int temp() {
    LOCAL_VAR(int, i_wont_show, 0);
    return i_wont_show;
}

int main(){
    LOCAL_VAR(long, l, 15);
    LOCAL_VAR(int, x, 192);
    LOCAL_VAR(short, y, 256);

    temp();

    l += 10;

    puts([[StackVariable dump] UTF8String]);
}

输出:

/Users/rross/Documents/TestProj/TestProj/main.m:676 - 长 l = { 19 00 00 00 00 00 00 00 }
/Users/rross/Documents/TestProj/TestProj/main.m:677 - int x = { c0 00 00 00 }
/Users/rross/Documents/TestProj/TestProj/main.m:678 - 短 y = { 00 01 }

这需要为您想要测试的任何文件启用 ARC(以及所有它的魔法),否则您将不得不手动释放__stack_变量,这不会很漂亮。

但是,它现在为您提供了变量的十六进制转储(而不是怪异的指针),如果您真的足够努力(使用__builtin_types_compatible),它可以检测结果是否是一个对象,并打印出来。

再一次,线程会搞砸这个问题,但解决这个问题的一个简单方法是创建 a NSDictionaryof NSArrays,并以 aNSThread作为键。让它有点慢,但老实说,如果你在 C++ 版本上使用它,你不会追求性能。

于 2013-05-10T11:55:44.740 回答
2

如果你愿意使用Objective-C++,那么这绝对是可能的,只要你也愿意以不同的方式声明你的变量,并且明白你只能用这种方法来抓取你自己的变量。

另请注意,它会使用额外的__stack_变量增加堆栈帧的大小,这可能会导致内存问题(尽管我个人对此表示怀疑)。

它不适用于某些结构,例如 for 循环,但对于 95% 的情况,这应该可以满足您的需要:

#include <vector>

struct stack_variable;
static std::vector<const stack_variable *> stack_variables;

struct stack_variable {
    void **_value;
    const char *_name;
    const char *_type;
    const char *_file;
    const char *_line;

private:    
    template<typename T>
    stack_variable(const T& value, const char *type, const char *name, const char *file, const char *line) : _value((void **) &value), _type(type),  _name(name), _file(file), _line(line) {
        add(*this);
    }

    static inline void add(const stack_variable &var) {
        stack_variables.push_back(static_cast<const stack_variable *>(&var));
    }

    static inline void remove(const stack_variable &var) {
        for (auto it = stack_variables.begin(); it != stack_variables.end(); it++) {
            if ((*it) == &var) {
                stack_variables.erase(it);
                return;
            }
        }
    }

public:
    template<typename T>
    static inline stack_variable create(const T& value, const char *type, const char *name, const char *file, const char *line) {
        return stack_variable(value, type, name, file, line);
    }

    ~stack_variable() {
        remove(*this);
    }

    void print() const {
        // treat the value as a pointer
        printf("%s:%s - %s %s = %p\n", _file, _line, _type, _name, *_value);
    }

    static void dump_vars() {
        for (auto var : stack_variables) {
            var->print();
        }
    }

};

#define __LINE_STR(LINE) #LINE
#define _LINE_STR(LINE) __LINE_STR(LINE)
#define LINE_STR _LINE_STR(__LINE__)

#define LOCAL_VAR(type, name, value)\
type name = value;\
stack_variable __stack_ ## name = stack_variable::create<type>(name, #type, #name, __FILE__, LINE_STR);\
(void) __stack_ ## name;

例子:

int temp() {
    LOCAL_VAR(int, i_wont_show, 0);
    return i_wont_show;
}


int main(){
    LOCAL_VAR(long, l, 15);
    LOCAL_VAR(int, x, 192);
    LOCAL_VAR(short, y, 256);

    temp();

    l += 10;

    stack_variable::dump_vars();
}

输出(请注意小于 sizeof(void *) 的值的垃圾额外字节,对此我无能为力):

/Users/rross/Documents/TestProj/TestProj/main.mm:672 - 长 l = 0x19
/Users/rross/Documents/TestProj/TestProj/main.mm:673 - int x = 0x5fbff8b8000000c0
/Users/rross/Documents/TestProj/TestProj/main.mm:674 - 短 y = 0xd000000010100

然而,线程将彻底搞砸,因此在多线程环境中,这(几乎)毫无价值。

于 2013-05-09T23:40:35.957 回答