溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

在軟件部署中如何使用strace進(jìn)行調(diào)試

發(fā)布時(shí)間:2021-10-23 14:30:59 來(lái)源:億速云 閱讀:151 作者:小新 欄目:系統(tǒng)運(yùn)維

這篇文章主要介紹在軟件部署中如何使用strace進(jìn)行調(diào)試,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

什么是 strace?

strace 是一個(gè)用來(lái)“追蹤系統(tǒng)調(diào)用”的工具。它主要是一個(gè) Linux 工具,但是你也可以在其它系統(tǒng)上使用類(lèi)似的工具(例如 DTrace 和 ktrace)。

它的基本用法非常簡(jiǎn)單。只需要在 strace 后面跟上你需要運(yùn)行的命令,它就會(huì)顯示出該命令觸發(fā)的所有系統(tǒng)調(diào)用(你可能需要先安裝好 strace):

$ strace echo Hello...Snip lots of stuff...write(1, "Hello\n", 6)                  = 6close(1)                                = 0close(2)                                = 0exit_group(0)                           = ?+++ exited with 0 +++

這些系統(tǒng)調(diào)用都是什么?它們就像是操作系統(tǒng)內(nèi)核提供的  API。很久以前,軟件擁有直接訪問(wèn)硬件的權(quán)限。如果軟件需要在屏幕上顯示一些東西,它將會(huì)與視頻硬件的端口和內(nèi)存映射寄存器糾纏不清。當(dāng)多任務(wù)操作系統(tǒng)變得流行以后,這就導(dǎo)致了混亂的局面,因?yàn)椴煌膽?yīng)用程序?qū)ⅰ盃?zhēng)奪”硬件,并且一個(gè)應(yīng)用程序的錯(cuò)誤可能致使其它應(yīng)用程序崩潰,甚至導(dǎo)致整個(gè)系統(tǒng)崩潰。所以  CPU  開(kāi)始支持多種不同的特權(quán)模式(或者稱(chēng)為“保護(hù)環(huán)”)。它們讓操作系統(tǒng)內(nèi)核在具有完全硬件訪問(wèn)權(quán)限的最高特權(quán)模式下運(yùn)行,于此同時(shí),其它在低特權(quán)模式下運(yùn)行的應(yīng)用程序必須通過(guò)向內(nèi)核發(fā)起系統(tǒng)調(diào)用才能夠與硬件進(jìn)行交互。

在二進(jìn)制級(jí)別上,發(fā)起系統(tǒng)調(diào)用相比簡(jiǎn)單的函數(shù)調(diào)用有一些區(qū)別,但是大部分程序都使用標(biāo)準(zhǔn)庫(kù)提供的封裝函數(shù)。例如,POSIX C 標(biāo)準(zhǔn)庫(kù)包含一個(gè) write() 函數(shù),該函數(shù)包含用于進(jìn)行 write 系統(tǒng)調(diào)用的所有與硬件體系結(jié)構(gòu)相關(guān)的代碼。

在軟件部署中如何使用strace進(jìn)行調(diào)試

簡(jiǎn)單來(lái)說(shuō),一個(gè)應(yīng)用程序與其環(huán)境(計(jì)算機(jī)系統(tǒng))的交互都是通過(guò)系統(tǒng)調(diào)用來(lái)完成的。所以當(dāng)軟件在一臺(tái)機(jī)器上可以工作但是在另一臺(tái)機(jī)器無(wú)法工作的時(shí)候,追蹤系統(tǒng)調(diào)用是一個(gè)很好的查錯(cuò)方法。具體地說(shuō),你可以通過(guò)追蹤系統(tǒng)調(diào)用分析以下典型操作:

  • 控制臺(tái)輸入與輸出 (IO)

  • 網(wǎng)絡(luò) IO

  • 文件系統(tǒng)訪問(wèn)以及文件 IO

  • 進(jìn)程/線程生命周期管理

  • 原始內(nèi)存管理

  • 訪問(wèn)特定的設(shè)備驅(qū)動(dòng)

什么時(shí)候可以使用 strace?

理論上,strace 適用于任何用戶(hù)空間程序,因?yàn)樗械挠脩?hù)空間程序都需要進(jìn)行系統(tǒng)調(diào)用。strace 對(duì)于已編譯的低級(jí)程序最有效果,但如果你可以避免運(yùn)行時(shí)環(huán)境和解釋器帶來(lái)的大量額外輸出,則仍然可以與 Python 等高級(jí)語(yǔ)言程序一起使用。

當(dāng)軟件在一臺(tái)機(jī)器上正常工作,但在另一臺(tái)機(jī)器上卻不能正常工作,同時(shí)拋出了有關(guān)文件、權(quán)限或者不能運(yùn)行某某命令等模糊的錯(cuò)誤信息時(shí),strace 往往能大顯身手。不幸的是,它不能診斷高等級(jí)的問(wèn)題,例如數(shù)字證書(shū)驗(yàn)證錯(cuò)誤等。這些問(wèn)題通常需要組合使用 strace(有時(shí)候是 ltrace)和其它高級(jí)工具(例如使用 openssl 命令行工具調(diào)試數(shù)字證書(shū)錯(cuò)誤)。

本文中的示例基于獨(dú)立的服務(wù)器,但是對(duì)系統(tǒng)調(diào)用的追蹤通常也可以在更復(fù)雜的部署平臺(tái)上完成,僅需要找到合適的工具。

一個(gè)簡(jiǎn)單的例子

假設(shè)你正在嘗試運(yùn)行一個(gè)叫做 foo 的服務(wù)器應(yīng)用程序,但是發(fā)生了以下情況:

$ fooError opening configuration file: No such file or directory

顯然,它沒(méi)有找到你已經(jīng)寫(xiě)好的配置文件。之所以會(huì)發(fā)生這種情況,是因?yàn)榘芾砉ぞ哂袝r(shí)候在編譯應(yīng)用程序時(shí)指定了自定義的路徑,所以你應(yīng)當(dāng)遵循特定發(fā)行版提供的安裝指南。如果錯(cuò)誤信息告訴你正確的配置文件應(yīng)該在什么地方,你就可以在幾秒鐘內(nèi)解決這個(gè)問(wèn)題,但如果沒(méi)有告訴你呢?你該如何找到正確的路徑?

如果你有權(quán)訪問(wèn)源代碼,則可以通過(guò)閱讀源代碼來(lái)解決問(wèn)題。這是一個(gè)好的備用計(jì)劃,但不是最快的解決方案。你還可以使用類(lèi)似 gdb 的單步調(diào)試器來(lái)觀察程序的行為,但使用專(zhuān)門(mén)用于展示程序與系統(tǒng)環(huán)境交互作用的工具 strace 更加有效。

一開(kāi)始, strace 產(chǎn)生的大量輸出可能會(huì)讓你不知所措,幸好你可以忽略其中大部分的無(wú)用信息。我經(jīng)常使用 -o 參數(shù)把輸出的追蹤結(jié)果保存到單獨(dú)的文件里:

$ strace -o /tmp/trace fooError opening configuration file: No such file or directory$ cat /tmp/traceexecve("foo", ["foo"], 0x7ffce98dc010 /* 16 vars */) = 0brk(NULL)                               = 0x56363b3fb000access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3fstat(3, {st_mode=S_IFREG|0644, st_size=25186, ...}) = 0mmap(NULL, 25186, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f2f12cf1000close(3)                                = 0openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260A\2\0\0\0\0\0"..., 832) = 832fstat(3, {st_mode=S_IFREG|0755, st_size=1824496, ...}) = 0mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2f12cef000mmap(NULL, 1837056, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f2f12b2e000mprotect(0x7f2f12b50000, 1658880, PROT_NONE) = 0mmap(0x7f2f12b50000, 1343488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f2f12b50000mmap(0x7f2f12c98000, 311296, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16a000) = 0x7f2f12c98000mmap(0x7f2f12ce5000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b6000) = 0x7f2f12ce5000mmap(0x7f2f12ceb000, 14336, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f2f12ceb000close(3)                                = 0arch_prctl(ARCH_SET_FS, 0x7f2f12cf0500) = 0mprotect(0x7f2f12ce5000, 16384, PROT_READ) = 0mprotect(0x56363b08b000, 4096, PROT_READ) = 0mprotect(0x7f2f12d1f000, 4096, PROT_READ) = 0munmap(0x7f2f12cf1000, 25186)           = 0openat(AT_FDCWD, "/etc/foo/config.json", O_RDONLY) = -1 ENOENT (No such file or directory)dup(2)                                  = 3fcntl(3, F_GETFL)                       = 0x2 (flags O_RDWR)brk(NULL)                               = 0x56363b3fb000brk(0x56363b41c000)                     = 0x56363b41c000fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x8), ...}) = 0write(3, "Error opening configuration file"..., 60) = 60close(3)                                = 0exit_group(1)                           = ?+++ exited with 1 +++

strace 輸出的第一頁(yè)通常是低級(jí)的進(jìn)程啟動(dòng)過(guò)程。(你可以看到很多 mmap、mprotectbrk 調(diào)用,這是用來(lái)分配原始內(nèi)存和映射動(dòng)態(tài)鏈接庫(kù)的。)實(shí)際上,在查找錯(cuò)誤時(shí),最好從下往上閱讀 strace 的輸出。你可以看到 write 調(diào)用在最后返回了錯(cuò)誤信息。如果你向上找,你將會(huì)看到第一個(gè)失敗的系統(tǒng)調(diào)用是 openat,它在嘗試打開(kāi) /etc/foo/config.json 時(shí)拋出了 ENOENT (“No such file or directory”)的錯(cuò)誤。現(xiàn)在我們已經(jīng)知道了配置文件應(yīng)該放在哪里。

這是一個(gè)簡(jiǎn)單的例子,但我敢說(shuō)在 90% 的情況下,使用 strace 進(jìn)行調(diào)試不需要更多復(fù)雜的工作。以下是完整的調(diào)試步驟:

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. 從程序中獲得含糊不清的錯(cuò)誤信息

  3. 使用 strace 運(yùn)行程序

  4. 在輸出中找到錯(cuò)誤信息

  5. 往前追溯并找到第一個(gè)失敗的系統(tǒng)調(diào)用

第四步中的系統(tǒng)調(diào)用很可能向你顯示出問(wèn)題所在。

小技巧

在開(kāi)始更加復(fù)雜的調(diào)試之前,這里有一些有用的調(diào)試技巧幫助你高效使用 strace

man 是你的朋友

在很多 *nix 操作系統(tǒng)中,你可以通過(guò) man syscalls 查看系統(tǒng)調(diào)用的列表。你將會(huì)看到類(lèi)似于 brk(2) 之類(lèi)的東西,這意味著你可以通過(guò)運(yùn)行 man 2 brk 得到與此相關(guān)的更多信息。

一個(gè)小問(wèn)題:man 2 fork 會(huì)顯示出在 GNU libc 里封裝的 fork() 手冊(cè)頁(yè),而 fork() 現(xiàn)在實(shí)際上是由 clone 系統(tǒng)調(diào)用實(shí)現(xiàn)的。fork 的語(yǔ)義與 clone 相同,但是如果我寫(xiě)了一個(gè)含有 fork() 的程序并使用 strace 去調(diào)試它,我將找不到任何關(guān)于 fork 調(diào)用的信息,只能看到 clone 調(diào)用。如果將源代碼與 strace 的輸出進(jìn)行比較的時(shí)候,像這種問(wèn)題會(huì)讓人感到困惑。

使用 -o 將輸出保存到文件

strace 可以生成很多輸出,所以將輸出保存到單獨(dú)的文件是很有幫助的(就像上面的例子一樣)。它還能夠在控制臺(tái)中避免程序自身的輸出與 strace 的輸出發(fā)生混淆。

使用 -s 查看更多的參數(shù)

你可能已經(jīng)注意到,錯(cuò)誤信息的第二部分沒(méi)有出現(xiàn)在上面的例子中。這是因?yàn)?strace 默認(rèn)僅顯示字符串參數(shù)的前 32 個(gè)字節(jié)。如果你需要捕獲更多參數(shù),請(qǐng)向 strace 追加類(lèi)似于 -s 128 之類(lèi)的參數(shù)。

-y 使得追蹤文件或套接字更加容易

“一切皆文件”意味著 *nix 系統(tǒng)通過(guò)文件描述符進(jìn)行所有 IO 操作,不管是真實(shí)的文件還是通過(guò)網(wǎng)絡(luò)或者進(jìn)程間管道。這對(duì)于編程而言是很方便的,但是在追蹤系統(tǒng)調(diào)用時(shí),你將很難分辨出 readwrite 的真實(shí)行為。

-y 參數(shù)使 strace 在注釋中注明每個(gè)文件描述符的具體指向。

使用 -p 附加到正在運(yùn)行的進(jìn)程中

正如我們將在后面的例子中看到的,有時(shí)候你想追蹤一個(gè)正在運(yùn)行的程序。如果你知道這個(gè)程序的進(jìn)程號(hào)為 1337 (可以通過(guò) ps 查詢(xún)),則可以這樣操作:

$ strace -p 1337...system call trace output...

你可能需要 root 權(quán)限才能運(yùn)行。

使用 -f 追蹤子進(jìn)程

strace 默認(rèn)只追蹤一個(gè)進(jìn)程。如果這個(gè)進(jìn)程產(chǎn)生了一個(gè)子進(jìn)程,你將會(huì)看到創(chuàng)建子進(jìn)程的系統(tǒng)調(diào)用(一般是 clone),但是你看不到子進(jìn)程內(nèi)觸發(fā)的任何調(diào)用。

如果你認(rèn)為在子進(jìn)程中存在錯(cuò)誤,則需要使用 -f 參數(shù)啟用子進(jìn)程追蹤功能。這樣做的缺點(diǎn)是輸出的內(nèi)容會(huì)讓人更加困惑。當(dāng)追蹤一個(gè)進(jìn)程時(shí),strace 顯示的是單個(gè)調(diào)用事件流。當(dāng)追蹤多個(gè)進(jìn)程的時(shí)候,你將會(huì)看到以 <unfinished ...> 開(kāi)始的初始調(diào)用,接著是一系列針對(duì)其它線程的調(diào)用,最后才出現(xiàn)以 <... foocall resumed> 結(jié)束的初始調(diào)用。此外,你可以使用 -ff 參數(shù)將所有的調(diào)用分離到不同的文件中(查看 strace 手冊(cè) 獲取更多信息)。

使用 -e 進(jìn)行過(guò)濾

正如你所看到的,默認(rèn)的追蹤輸出是所有的系統(tǒng)調(diào)用。你可以使用 -e 參數(shù)過(guò)濾你需要追蹤的調(diào)用(查看 strace 手冊(cè))。這樣做的好處是運(yùn)行過(guò)濾后的 strace 比起使用 grep 進(jìn)行二次過(guò)濾要更快。老實(shí)說(shuō),我大部分時(shí)間都不會(huì)被打擾。

并非所有的錯(cuò)誤都是不好的

一個(gè)簡(jiǎn)單而常用的例子是一個(gè)程序在多個(gè)位置搜索文件,例如 shell 搜索哪個(gè) bin/ 目錄包含可執(zhí)行文件:

$ strace sh -c uname...stat("/home/user/bin/uname", 0x7ffceb817820) = -1 ENOENT (No such file or directory)stat("/usr/local/bin/uname", 0x7ffceb817820) = -1 ENOENT (No such file or directory)stat("/usr/bin/uname", {st_mode=S_IFREG|0755, st_size=39584, ...}) = 0...

“錯(cuò)誤信息之前的最后一次失敗調(diào)用”這種啟發(fā)式方法非常適合于查找錯(cuò)誤。無(wú)論如何,自下而上地查找是有道理的。

C 編程指南非常有助于理解系統(tǒng)調(diào)用

標(biāo)準(zhǔn) C 庫(kù)函數(shù)調(diào)用不屬于系統(tǒng)調(diào)用,但它們僅是系統(tǒng)調(diào)用之上的唯一一個(gè)薄層。所以如果你了解(甚至只是略知一二)如何使用 C 語(yǔ)言,那么閱讀系統(tǒng)調(diào)用追蹤信息就非常容易。例如,如果你在調(diào)試網(wǎng)絡(luò)系統(tǒng)調(diào)用,你可以嘗試略讀 Beej 經(jīng)典的《網(wǎng)絡(luò)編程指南》。

一個(gè)更復(fù)雜的調(diào)試?yán)?/h4>

就像我說(shuō)的那樣,簡(jiǎn)單的調(diào)試?yán)颖憩F(xiàn)了我在大部分情況下如何使用 strace。然而,有時(shí)候需要一些更加細(xì)致的工作,所以這里有一個(gè)稍微復(fù)雜(且真實(shí))的例子。

bcron 是一個(gè)任務(wù)調(diào)度器,它是經(jīng)典 *nix cron 守護(hù)程序的另一種實(shí)現(xiàn)。它已經(jīng)被安裝到一臺(tái)服務(wù)器上,但是當(dāng)有人嘗試編輯作業(yè)時(shí)間表時(shí),發(fā)生了以下情況:

# crontab -e -u logsbcrontab: Fatal: Could not create temporary file

好的,現(xiàn)在 bcron 嘗試寫(xiě)入一些文件,但是它失敗了,也沒(méi)有告訴我們?cè)?。以下?strace 的輸出:

# strace -o /tmp/trace crontab -e -u logsbcrontab: Fatal: Could not create temporary file# cat /tmp/trace...openat(AT_FDCWD, "bcrontab.14779.1573691864.847933", O_RDONLY) = 3mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f82049b4000read(3, "#Ansible: logsagg\n20 14 * * * lo"..., 8192) = 150read(3, "", 8192)                       = 0munmap(0x7f82049b4000, 8192)            = 0close(3)                                = 0socket(AF_UNIX, SOCK_STREAM, 0)         = 3connect(3, {sa_family=AF_UNIX, sun_path="/var/run/bcron-spool"}, 110) = 0mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f82049b4000write(3, "156:Slogs\0#Ansible: logsagg\n20 1"..., 161) = 161read(3, "32:ZCould not create temporary f"..., 8192) = 36munmap(0x7f82049b4000, 8192)            = 0close(3)                                = 0write(2, "bcrontab: Fatal: Could not creat"..., 49) = 49unlink("bcrontab.14779.1573691864.847933") = 0exit_group(111)                         = ?+++ exited with 111 +++

在程序結(jié)束之前有一個(gè) write 的錯(cuò)誤信息,但是這次有些不同。首先,在此之前沒(méi)有任何相關(guān)的失敗系統(tǒng)調(diào)用。其次,我們看到這個(gè)錯(cuò)誤信息是由 read 從別的地方讀取而來(lái)的。這看起來(lái)像是真正的錯(cuò)誤發(fā)生在別的地方,而 bcrontab 只是在轉(zhuǎn)播這些信息。

如果你查閱了 man 2 read,你將會(huì)看到 read 的第一個(gè)參數(shù) (3) 是一個(gè)文件描述符,這是 *nix 操作系統(tǒng)用于所有 IO 操作的句柄。你該如何知道文件描述符 3 代表什么?在這種情況下,你可以使用 -y 參數(shù)運(yùn)行 strace(如上文所述),它將會(huì)在注釋里告訴你文件描述符的具體指向,但是了解如何從上面這種輸出中分析追蹤結(jié)果是很有用的。

一個(gè)文件描述符可以來(lái)自于許多系統(tǒng)調(diào)用之一(這取決于它是用于控制臺(tái)、網(wǎng)絡(luò)套接字還是真實(shí)文件等的描述符),但不論如何,我們都可以搜索返回值為 3 的系統(tǒng)調(diào)用(例如,在 strace 的輸出中查找 =3)。在這次 strace 中可以看到有兩個(gè)這樣的調(diào)用:最上面的 openat 以及中間的 socket。openat 打開(kāi)一個(gè)文件,但是緊接著的 close(3) 表明其已經(jīng)被關(guān)閉。(注意:文件描述符可以在打開(kāi)并關(guān)閉后重復(fù)使用。)所以 socket 調(diào)用才是與此相關(guān)的(它是在 read 之前的最后一個(gè)),這告訴我們 brcontab 正在與一個(gè)網(wǎng)絡(luò)套接字通信。在下一行,connect 表明文件描述符 3 是一個(gè)連接到 /var/run/bcron-spool 的 Unix 域套接字。

因此,我們需要弄清楚 Unix 套接字的另一側(cè)是哪個(gè)進(jìn)程在監(jiān)聽(tīng)。有兩個(gè)巧妙的技巧適用于在服務(wù)器部署中調(diào)試。一個(gè)是使用 netstat 或者較新的 ss。這兩個(gè)命令都描述了當(dāng)前系統(tǒng)中活躍的網(wǎng)絡(luò)套接字,使用 -l 參數(shù)可以顯示出處于監(jiān)聽(tīng)狀態(tài)的套接字,而使用 -p 參數(shù)可以得到正在使用該套接字的程序信息。(它們還有更多有用的選項(xiàng),但是這兩個(gè)已經(jīng)足夠完成工作了。)

# ss -pl | grep /var/run/bcron-spoolu_str LISTEN 0   128   /var/run/bcron-spool 1466637   * 0   users:(("unixserver",pid=20629,fd=3))

這告訴我們 /var/run/bcron-spool 套接字的監(jiān)聽(tīng)程序是 unixserver 這個(gè)命令,它的進(jìn)程 ID 為 20629。(巧合的是,這個(gè)程序也使用文件描述符 3 去連接這個(gè)套接字。)

第二個(gè)常用的工具就是使用 lsof 查找相同的信息。它可以列出當(dāng)前系統(tǒng)中打開(kāi)的所有文件(或文件描述符)?;蛘?,我們可以得到一個(gè)具體文件的信息:

# lsof /var/run/bcron-spoolCOMMAND   PID   USER  FD  TYPE  DEVICE              SIZE/OFF  NODE    NAMEunixserve 20629 cron  3u  unix  0x000000005ac4bd83  0t0       1466637 /var/run/bcron-spool type=STREAM

進(jìn)程 20629 是一個(gè)常駐進(jìn)程,所以我們可以使用 strace -o /tmp/trace -p 20629 去查看該進(jìn)程的系統(tǒng)調(diào)用。如果我們?cè)诹硪粋€(gè)終端嘗試編輯 cron 的計(jì)劃任務(wù)表,就可以在錯(cuò)誤發(fā)生時(shí)捕獲到以下信息:

accept(3, NULL, NULL)                   = 4clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21181close(4)                                = 0accept(3, NULL, NULL)                   = ? ERESTARTSYS (To be restarted if SA_RESTART is set)--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21181, si_uid=998, si_status=0, si_utime=0, si_stime=0} ---wait4(0, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED, NULL) = 21181wait4(0, 0x7ffe6bc36764, WNOHANG|WSTOPPED, NULL) = -1 ECHILD (No child processes)rt_sigaction(SIGCHLD, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, 8) = 0rt_sigreturn({mask=[]})                 = 43accept(3, NULL, NULL)                   = 4clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21200close(4)                                = 0accept(3, NULL, NULL)                   = ? ERESTARTSYS (To be restarted if SA_RESTART is set)--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21200, si_uid=998, si_status=111, si_utime=0, si_stime=0} ---wait4(0, [{WIFEXITED(s) && WEXITSTATUS(s) == 111}], WNOHANG|WSTOPPED, NULL) = 21200wait4(0, 0x7ffe6bc36764, WNOHANG|WSTOPPED, NULL) = -1 ECHILD (No child processes)rt_sigaction(SIGCHLD, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, 8) = 0rt_sigreturn({mask=[]})                 = 43accept(3, NULL, NULL

(最后一個(gè) accept 調(diào)用沒(méi)有在追蹤期間完成。)不幸的是,這次追蹤沒(méi)有包含我們想要的錯(cuò)誤信息。我們沒(méi)有觀察到 bcrontan 往套接字發(fā)送或接受的任何信息。然而,我們看到了很多進(jìn)程管理操作(clone、wait4、SIGCHLD,等等)。這個(gè)進(jìn)程產(chǎn)生了子進(jìn)程,我們猜測(cè)真實(shí)的工作是由子進(jìn)程完成的。如果我們想捕獲子進(jìn)程的追蹤信息,就必須往 strace 追加 -f 參數(shù)。以下是我們最終使用 strace -f -o /tmp/trace -p 20629 找到的錯(cuò)誤信息:

21470 openat(AT_FDCWD, "tmp/spool.21470.1573692319.854640", O_RDWR|O_CREAT|O_EXCL, 0600) = -1 EACCES (Permission denied)21470 write(1, "32:ZCould not create temporary f"..., 36) = 3621470 write(2, "bcron-spool[21470]: Fatal: logs:"..., 84) = 8421470 unlink("tmp/spool.21470.1573692319.854640") = -1 ENOENT (No such file or directory)21470 exit_group(111)                   = ?21470 +++ exited with 111 +++

現(xiàn)在我們知道了進(jìn)程 ID 21470 在嘗試創(chuàng)建文件 tmp/spool.21470.1573692319.854640 (相對(duì)于當(dāng)前的工作目錄)時(shí)得到了一個(gè)沒(méi)有權(quán)限的錯(cuò)誤。如果我們知道當(dāng)前的工作目錄,就可以得到完整路徑并能指出為什么該進(jìn)程無(wú)法在此處創(chuàng)建臨時(shí)文件。不幸的是,這個(gè)進(jìn)程已經(jīng)退出了,所以我們不能使用 lsof -p 21470 去找出當(dāng)前的工作目錄,但是我們可以往前追溯,查找進(jìn)程 ID 21470 使用哪個(gè)系統(tǒng)調(diào)用改變了它的工作目錄。這個(gè)系統(tǒng)調(diào)用是 chdir(可以在搜索引擎很輕松地找到)。以下是一直往前追溯到服務(wù)器進(jìn)程 ID 20629 的結(jié)果:

20629 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21470...21470 execve("/usr/sbin/bcron-spool", ["bcron-spool"], 0x55d2460807e0 /* 27 vars */) = 0...21470 chdir("/var/spool/cron")          = 0...21470 openat(AT_FDCWD, "tmp/spool.21470.1573692319.854640", O_RDWR|O_CREAT|O_EXCL, 0600) = -1 EACCES (Permission denied)21470 write(1, "32:ZCould not create temporary f"..., 36) = 3621470 write(2, "bcron-spool[21470]: Fatal: logs:"..., 84) = 8421470 unlink("tmp/spool.21470.1573692319.854640") = -1 ENOENT (No such file or directory)21470 exit_group(111)                   = ?21470 +++ exited with 111 +++

(如果你在這里迷糊了,你可能需要閱讀 我之前有關(guān) *nix 進(jìn)程管理和 shell 的文章)

現(xiàn)在 PID 為 20629 的服務(wù)器進(jìn)程沒(méi)有權(quán)限在 /var/spool/cron/tmp/spool.21470.1573692319.854640 創(chuàng)建文件。最可能的原因就是典型的 *nix 文件系統(tǒng)權(quán)限設(shè)置。讓我們檢查一下:

# ls -ld /var/spool/cron/tmp/drwxr-xr-x 2 root root 4096 Nov  6 05:33 /var/spool/cron/tmp/# ps u -p 20629USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMANDcron     20629  0.0  0.0   2276   752 ?        Ss   Nov14   0:00 unixserver -U /var/run/bcron-spool -- bcron-spool

這就是問(wèn)題所在!這個(gè)服務(wù)進(jìn)程以 cron 用戶(hù)運(yùn)行,但是只有 root 用戶(hù)才有向 /var/spool/cron/tmp/ 目錄寫(xiě)入的權(quán)限。一個(gè)簡(jiǎn)單 chown cron /var/spool/cron/tmp/ 命令就能讓 bcron 正常工作。

以上是“在軟件部署中如何使用strace進(jìn)行調(diào)試”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI