為了除錯 macOS 程式,我需要列印NSRect
傳遞給-[NSView:setNeedsDisplayInRect:]的內容。我可以在該方法中設定斷點,但我無法列印它的引數。
NSRect
“本質上”是四個double
s 的結構。為了演示這個問題,我撰寫了一個獨立的小程式。它使用 Xcode 12.5 編譯并在配備 M1 處理器的 Mac mini 上運行。
#include <stdio.h>
typedef struct {
double a;
double b;
double c;
double d;
} MyRect1;
typedef struct {
double a;
double b;
double c;
long d;
} MyRect2;
void foo(MyRect1 rect) {
printf("%f\n", rect.a);
}
void bar(MyRect2 rect) {
printf("%f\n", rect.a);
}
int main(int argc, const char * argv[]) {
MyRect1 rect1 = { 1, 2, 3, 4 };
MyRect2 rect2 = { 1, 2, 3, 4 };
foo(rect1);
bar(rect2);
return 0;
}
ARM 64 位架構的程序呼叫標準 (AArch64)指出
如果引數型別是大于 16 位元組的復合型別,則將引數復制到呼叫者分配的記憶體中,并將引數替換為指向副本的指標。
因此,我希望這兩個foo()
和bar()
都使用指向結構的指標作為單個引數來呼叫。設定斷點bar()
并將第一個引數強制轉換為MyRect2 *
確實會產生預期的結果:
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x0000000100003e94 argtest2`bar(rect=(a = 1, b = 2, c = 3, d = 4)) at main.c:22:29
frame #1: 0x0000000100003f34 argtest2`main(argc=1, argv=0x000000016fdff428) at main.c:29:9
frame #2: 0x000000019871d430 libdyld.dylib`start 4
(lldb) expr -- *(MyRect2 *)$arg1
(MyRect2) $0 = (a = 1, b = 2, c = 3, d = 4)
但是,這不適用于MyRect1
in foo()
:
lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x0000000100003e64 argtest2`foo(rect=(a = 1, b = 2, c = 3, d = 4)) at main.c:18:29
frame #1: 0x0000000100003f1c argtest2`main(argc=1, argv=0x000000016fdff428) at main.c:28:9
frame #2: 0x000000019871d430 libdyld.dylib`start 4
(lldb) expr -- *(MyRect1 *)$arg1
error: Couldn't apply expression side effects : Couldn't dematerialize a result variable: couldn't read its memory
請注意,兩個結構具有相同的大小(32 位元組),并且僅在最后一個成員中有所不同。
問題:NSRect
如果該引數是型別(或任何其他四個雙精度的結構),我如何列印傳遞給 lldb 中函式的引數?
uj5u.com熱心網友回復:
在你的結構中長期切換雙倍實際上很重要。四個雙精度的結構是同構浮點聚合,而三個雙精度和一個長的結構是復合型別,它們有不同的傳遞規則。如果合適(如您所見),前者將在浮點暫存器中傳遞,而后者則在堆疊中傳遞。
順便說一句,如果您想讀取傳遞給您沒有除錯資訊的函式的值,最好在“函式序言”執行之前停止。函式序言(尤其是在優化代碼中)通常會從其原始位置復制暫存器并重用它們,因此序言之后的暫存器狀態將不再反映呼叫約定。
但是,在序言中,函式的除錯資訊通常不準確。沒有技術原因不能這樣做,但是我知道沒有編譯器跟蹤序言,除非您實際上正在除錯編譯器輸出,否則該代碼區域并不是非常有趣。所以對于帶有除錯資訊的代碼,在序言執行后停止會更方便,這是 lldb 的默認設定。
要切換該默認值,請添加--skip-prologue 0
到您的break set
命令。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/508597.html