اشکال زدایی استقرار نرم افزار با strace

اشکال زدایی استقرار نرم افزار با strace

کار روزانه من عمدتاً استقرار نرم افزار است، به این معنی که زمان زیادی را صرف پاسخ دادن به سؤالاتی می کنم:

  • این نرم افزار برای توسعه دهنده کار می کند، اما برای من نه. چرا؟
  • دیروز این نرم افزار برای من کار می کرد، اما امروز این کار را نمی کند. چرا؟

این نوعی اشکال زدایی است که کمی با اشکال زدایی نرم افزار معمولی متفاوت است. اشکال زدایی منظم در مورد منطق کد است، اما اشکال زدایی استقرار در مورد تعامل بین کد و محیط است. حتی اگر ریشه مشکل یک خطای منطقی باشد، این واقعیت که همه چیز روی یک ماشین کار می کند و روی ماشین دیگر کار نمی کند به این معنی است که مشکل به نوعی در محیط است.

بنابراین به جای ابزارهای اشکال زدایی معمول مانند gdb من مجموعه متفاوتی از ابزارها برای استقرار اشکال زدایی دارم. و ابزار مورد علاقه من برای مقابله با مشکلی مانند "چرا این نرم افزار برای من کار نمی کند؟" تماس گرفت تسمه.

استریس چیست؟

تسمه ابزاری برای "ردیابی تماس های سیستمی" است. در ابتدا برای لینوکس ایجاد شد، اما همان ترفندهای اشکال زدایی را می توان با ابزارهایی برای سیستم های دیگر انجام داد (DTrace یا ktrace).

برنامه اصلی بسیار ساده است. شما فقط باید strace را با هر دستوری اجرا کنید و تمام تماس های سیستم را حذف می کند (البته ابتدا احتمالاً باید خودتان آن را نصب کنید. تسمه):

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

این نام های سیستم چیست؟ این چیزی شبیه یک API برای هسته سیستم عامل است. روزی روزگاری نرم افزار به سخت افزاری که روی آن کار می کرد دسترسی مستقیم داشت. برای مثال، اگر نیاز به نمایش چیزی روی صفحه داشت، با پورت‌ها یا رجیسترهای حافظه‌دار برای دستگاه‌های ویدیویی بازی می‌کرد. هنگامی که سیستم های کامپیوتری چندوظیفه ای رایج شدند، هرج و مرج حاکم شد زیرا برنامه های کاربردی مختلف بر سر سخت افزار مبارزه می کردند. خطاها در یک برنامه می‌توانند سایر برنامه‌ها، اگر نه کل سیستم را از بین ببرند. سپس حالت های امتیاز (یا "حفاظت حلقه") در CPU ظاهر شد. هسته ممتازترین شد: دسترسی کامل به سخت‌افزار را دریافت کرد و برنامه‌های کاربردی کمتری را ایجاد کرد که قبلاً مجبور بودند برای تعامل با سخت‌افزار از طریق تماس‌های سیستمی، از هسته درخواست دسترسی داشته باشند.

در سطح باینری، یک فراخوانی سیستم کمی متفاوت از یک فراخوانی تابع ساده است، اما اکثر برنامه ها از یک پوشش در کتابخانه استاندارد استفاده می کنند. آن ها کتابخانه استاندارد POSIX C شامل یک فراخوانی تابع است نوشتن()، که شامل تمام کدهای معماری خاص برای فراخوانی سیستم است نوشتن.

اشکال زدایی استقرار نرم افزار با strace

به طور خلاصه، هرگونه تعامل بین یک برنامه کاربردی و محیط آن (سیستم های کامپیوتری) از طریق فراخوانی سیستم انجام می شود. بنابراین، وقتی نرم‌افزار روی یک ماشین کار می‌کند اما روی ماشین دیگر کار نمی‌کند، خوب است به نتایج ردیابی تماس سیستم نگاه کنید. به طور خاص، در اینجا لیستی از نقاط معمولی است که می توان با استفاده از ردیابی فراخوانی سیستم تجزیه و تحلیل کرد:

  • ورودی/خروجی کنسول
  • شبکه ورودی/خروجی
  • دسترسی به سیستم فایل و ورودی/خروجی فایل
  • مدیریت طول عمر یک موضوع فرآیند
  • مدیریت حافظه در سطح پایین
  • دسترسی به درایورهای دستگاه خاص

چه زمانی از استریس استفاده کنیم؟

در تئوری، تسمه با هر برنامه ای در فضای کاربری استفاده می شود، زیرا هر برنامه ای در فضای کاربر باید تماس های سیستمی را انجام دهد. با برنامه های کامپایل شده و سطح پایین کارآمدتر کار می کند، اما اگر بتوانید نویز اضافی از زمان اجرا و مفسر را کاهش دهید، با زبان های سطح بالا مانند پایتون نیز کار می کند.

با تمام شکوه و عظمتش تسمه در هنگام اشکال زدایی نرم افزاری که به خوبی روی یک ماشین کار می کند، اما ناگهان روی دستگاه دیگر کار نمی کند، پیام های مبهم در مورد فایل ها، مجوزها یا تلاش های ناموفق برای اجرای برخی دستورات یا چیز دیگری تولید می کند... حیف است، اما اینطور نیست. به خوبی با مشکلات سطح بالا مانند خطاهای تأیید گواهی ترکیب می شود. معمولاً این نیاز به یک ترکیب دارد تسمهگاهی ردیابی و ابزارهای سطح بالاتر (مانند ابزار خط فرمان openssl برای رفع اشکال گواهی).

ما از یک سرور مستقل به عنوان مثال استفاده خواهیم کرد، اما ردیابی تماس های سیستمی اغلب می تواند در پلتفرم های استقرار پیچیده تر انجام شود. فقط باید ابزار مناسب را انتخاب کنید.

نمونه اشکال زدایی ساده

فرض کنید می‌خواهید برنامه سرور شگفت‌انگیز foo را اجرا کنید، و این همان چیزی است که به آن می‌رسید:

$ foo
Error opening configuration file: No such file or directory

ظاهرا فایل تنظیماتی که نوشتی رو پیدا نکرد. این به این دلیل اتفاق می افتد که گاهی اوقات وقتی مدیران بسته یک برنامه کاربردی را کامپایل می کنند، مکان های مورد انتظار فایل را لغو می کنند. و اگر راهنمای نصب را برای یک توزیع دنبال کنید، در توزیع دیگر فایل هایی کاملاً متفاوت از آنچه انتظار داشتید پیدا می کنید. اگر پیغام خطا بگوید کجا باید فایل پیکربندی را جست و جو کنید، مشکل را می توان در چند ثانیه حل کرد، اما اینطور نیست. پس کجا نگاه کنیم؟

اگر به کد منبع دسترسی دارید، می توانید آن را بخوانید و همه چیز را پیدا کنید. یک طرح پشتیبان خوب، اما نه سریع ترین راه حل. شما می توانید به یک دیباگر گام به گام مانند متوسل شوید gdb و ببینید که برنامه چه می کند، اما استفاده از ابزاری که به طور خاص برای نشان دادن تعامل با محیط طراحی شده است بسیار موثرتر است: تسمه.

نتیجه تسمه ممکن است زائد به نظر برسد، اما خبر خوب این است که بیشتر آن را می توان با خیال راحت نادیده گرفت. اغلب استفاده از عملگر -o برای ذخیره نتایج ردیابی در یک فایل جداگانه مفید است:

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

تقریبا کل صفحه اول خروجی تسمه - این معمولاً آماده سازی سطح پایین برای راه اندازی است. (تماس های زیاد نقشه, mprotec, برک برای مواردی مانند شناسایی حافظه سطح پایین و نمایش کتابخانه های پویا.) در واقع، در هنگام اشکال زدایی خروجی تسمه بهتر است از آخر بخوانید. در زیر یک چالش وجود خواهد داشت نوشتن، که پیغام خطا را نمایش می دهد. ما به بالا نگاه می کنیم و اولین تماس اشتباه سیستم - تماس را می بینیم باز کردن، که خطا می دهد ENOENT ("پرونده یا دایرکتوری یافت نشد") در حال تلاش برای باز کردن /etc/foo/config.json. این جایی است که فایل پیکربندی باید باشد.

این فقط یک مثال بود، اما می‌توانم بگویم 90 درصد مواقعی که استفاده می‌کنم تسمه، هیچ کاری سخت تر از این وجود ندارد. در زیر راهنمای کامل رفع اشکال گام به گام آورده شده است:

  • به دلیل یک پیام مبهم در مورد یک خطای system-y از یک برنامه ناراحت شوید
  • راه اندازی مجدد برنامه با تسمه
  • پیام خطا را در نتایج ردیابی پیدا کنید
  • بالاتر بروید تا اولین تماس ناموفق سیستمی را بزنید

بسیار محتمل است که فراخوانی سیستم در مرحله 4 اشتباه را نشان دهد.

نکات

قبل از اینکه نمونه ای از اشکال زدایی پیچیده تر را به شما نشان دهم، چند ترفند برای استفاده موثر به شما نشان خواهم داد تسمه:

مرد دوست شماست

در بسیاری از سیستم‌های *nix، می‌توان فهرست کاملی از فراخوانی‌های سیستم به هسته را با اجرا به دست آورد مرد syscalls. چیزهایی از این قبیل را خواهید دید brk (2)، به این معنی که اطلاعات بیشتری را می توان با اجرا به دست آورد مرد 2 برک.

چنگک کوچک: مرد 2 چنگال صفحه پوسته را به من نشان می دهد چنگال() в گنو libc، که به نظر می رسد با فراخوانی اجرا می شود شبیه(). معناشناسی را فراخوانی کنید چنگال اگر برنامه ای را با استفاده از آن بنویسید، ثابت می ماند چنگال()، و یک ردیابی را اجرا کنید - هیچ تماسی پیدا نمی کنم چنگال، به جای آنها وجود خواهد داشت شبیه(). چنین چنگک‌هایی تنها زمانی شما را گیج می‌کنند که شروع به مقایسه منبع با خروجی کنید تسمه.

برای ذخیره خروجی در یک فایل از -o استفاده کنید

تسمه می تواند خروجی گسترده ای تولید کند، بنابراین اغلب مفید است که نتایج ردیابی را در فایل های جداگانه ذخیره کنید (مانند مثال بالا). این همچنین به جلوگیری از اشتباه گرفتن خروجی برنامه با خروجی کمک می کند تسمه در کنسول

برای مشاهده داده های آرگومان بیشتر از -s استفاده کنید

ممکن است متوجه شده باشید که نیمه دوم پیام خطا در ردیابی مثال بالا نشان داده نشده است. به این دلیل است تسمه پیش فرض فقط 32 بایت اول آرگومان رشته را نشان می دهد. اگر می خواهید بیشتر ببینید، چیزی شبیه به آن اضافه کنید -s 128 به تماس تسمه.

-y ردیابی فایل ها، سوکت ها و غیره را آسان تر می کند.

"همه فایل است" به این معنی است که *سیستم‌های nix همه ورودی/خروجی را با استفاده از توصیفگرهای فایل انجام می‌دهند، خواه این مورد برای فایل یا شبکه یا لوله‌های بین پردازشی اعمال شود. این برای برنامه نویسی مناسب است، اما ردیابی آنچه را که واقعاً در جریان است، با مشاهده مشترک دشوار می کند خواندن и نوشتن در سیستم با نتایج ردیابی تماس بگیرید.

با اضافه کردن یک اپراتور بله، شما مجبور خواهید شد تسمه هر توصیفگر فایل را در خروجی با یادداشتی از آنچه به آن اشاره می کند، حاشیه نویسی کنید.

با -p** به یک فرآیند از قبل در حال اجرا پیوست کنید

همانطور که در مثال زیر مشاهده خواهید کرد، گاهی اوقات شما نیاز به ردیابی برنامه ای دارید که از قبل در حال اجرا است. اگر مشخص باشد که به عنوان فرآیند 1337 در حال اجراست (مثلاً از خروجی ps، سپس می توانید آن را به صورت زیر ردیابی کنید:

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

ممکن است به حقوق ریشه نیاز داشته باشید.

از -f برای نظارت بر فرآیندهای فرزند استفاده کنید

تسمه به طور پیش فرض، تنها یک فرآیند را ردیابی می کند. اگر این فرآیند فرآیندهای فرزند را ایجاد کند، آنگاه فراخوانی سیستم برای ایجاد فرآیند فرزند قابل مشاهده است، اما فراخوانی‌های سیستمی پردازش فرزند نمایش داده نمی‌شوند.

اگر فکر می‌کنید خطا در فرآیند فرزند است، از عبارت استفاده کنید -f، این ردیابی آن را فعال می کند. نقطه ضعف این است که خروجی شما را بیشتر گیج می کند. چه زمانی تسمه یک فرآیند یا یک رشته را ردیابی می کند، یک جریان واحد از رویدادهای فراخوانی را نشان می دهد. هنگامی که چندین فرآیند را به طور همزمان ردیابی می کند، ممکن است شروع تماسی را ببینید که با یک پیام قطع شده است ، سپس - تعداد زیادی فراخوان برای شاخه های اجرایی دیگر، و تنها پس از آن - پایان اولین مورد <…تماس فوکوس از سر گرفته شد>. یا تمام نتایج ردیابی را با استفاده از عملگر به فایل های مختلف تقسیم کنید -ff (جزئیات در رهبری بر تسمه).

ردیابی ها را با استفاده از -e فیلتر کنید

همانطور که می بینید، نتیجه ردیابی یک انبوه واقعی از همه فراخوانی های سیستمی ممکن است. پرچم -e می توانید ردیابی را فیلتر کنید (نگاه کنید به رهبری بر تسمه). مزیت اصلی این است که اجرای یک ردیابی فیلتر شده سریعتر از انجام یک ردیابی کامل و سپس انجام آن است grep استفادهدر صادقانه بگویم، تقریباً همیشه اهمیتی نمی‌دهم.

همه اشتباهات بد نیستند

یک مثال ساده و متداول برنامه ای است که به دنبال یک فایل در چندین مکان در یک زمان است، مانند پوسته ای که به دنبال دایرکتوری است که حاوی یک فایل اجرایی است:

$ 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
...

اکتشافی مانند "آخرین درخواست شکست خورده قبل از گزارش یک خطا" در یافتن خطاهای مرتبط خوب است. هر طور که باشد، منطقی است که از آخر شروع کنیم.

آموزش های برنامه نویسی C می تواند به شما در درک تماس های سیستمی کمک کند.

تماس های استاندارد به کتابخانه های C فراخوانی سیستمی نیستند، بلکه فقط یک لایه سطحی نازک هستند. بنابراین، اگر حداقل کمی درک کنید که چگونه و چه کاری باید در C انجام دهید، درک نتایج ردیابی فراخوانی سیستم برای شما آسان تر خواهد بود. به عنوان مثال، شما در اشکال زدایی تماس های سیستم های شبکه مشکل دارید، به همان کلاسیک نگاه کنید راهنمای بیجا برای برنامه نویسی شبکه.

یک مثال اشکال زدایی پیچیده تر

قبلاً گفتم که مثال اشکال زدایی ساده نمونه ای از مواردی است که من بیشتر در هنگام کار با آن دست و پنجه نرم می کنم تسمه. با این حال، گاهی اوقات یک تحقیق واقعی مورد نیاز است، بنابراین در اینجا یک مثال واقعی از اشکال زدایی پیشرفته تر آورده شده است.

bcron - زمانبندی پردازش وظایف، اجرای دیگری از *nix daemon cron را. بر روی سرور نصب شده است، اما زمانی که شخصی سعی می کند برنامه را ویرایش کند، این اتفاق می افتد:

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

باشه یعنی bcron سعی کرد فایل خاصی بنویسد، اما نتیجه نداد، و او دلیل آن را نمی پذیرد. کشف کردن تسمه:

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

یک پیغام خطایی نزدیک به پایان وجود دارد نوشتن، اما این بار چیزی متفاوت است. اول، هیچ خطای مربوط به فراخوانی سیستم وجود ندارد، که معمولا قبل از این اتفاق می افتد. ثانیاً مشخص است که در جایی شخصی قبلاً پیام خطا را خوانده است. به نظر می رسد مشکل واقعی جای دیگری است، و bcrontab به سادگی پیام را پخش می کند.

اگر نگاه کنید مرد 2 خواند، می بینید که آرگومان اول (3) یک توصیفگر فایل است که *nix برای تمام پردازش های I/O استفاده می کند. چگونه می توانم بفهمم که توصیفگر فایل 3 چه چیزی را نشان می دهد؟ در این مورد خاص، می توانید اجرا کنید تسمه با اپراتور بله (به بالا مراجعه کنید) و به طور خودکار به شما می گوید، اما برای فهمیدن مواردی مانند این، دانستن نحوه خواندن و تجزیه نتایج ردیابی مفید است.

منبع یک توصیفگر فایل می تواند یکی از بسیاری از فراخوانی های سیستمی باشد (همه به این بستگی دارد که توصیفگر برای چه چیزی باشد - یک کنسول، یک سوکت شبکه، خود فایل یا چیز دیگری)، اما به هر حال ما به دنبال آن هستیم. با برگرداندن 3 تماس می گیرد (یعنی ما به دنبال "= 3" در نتایج ردیابی هستیم). در این نتیجه 2 مورد از آنها وجود دارد: باز کردن در بالا و پریز در وسط. باز کردن فایل را باز می کند اما نزدیک(3) سپس نشان می دهد که دوباره بسته می شود. (Rake: توصیف‌کننده‌های فایل می‌توانند پس از باز و بسته شدن دوباره مورد استفاده قرار گیرند). زنگ زدن سوکت () مناسب است زیرا آخرین مورد قبلی است خواندن()، و معلوم می شود که bcrontab با چیزی از طریق یک سوکت کار می کند. خط بعدی نشان می دهد که توصیفگر فایل با آن مرتبط است سوکت دامنه یونیکس در طول مسیر /var/run/bcron-spool.

بنابراین، ما باید فرآیند مرتبط با آن را پیدا کنیم سوکت یونیکس از طرف دیگر. چند ترفند ساده برای این منظور وجود دارد که هر دوی آنها برای اشکال زدایی استقرار سرور مفید هستند. اولین مورد استفاده است netstat یا جدیدتر ss (وضعیت سوکت). هر دو فرمان اتصالات شبکه فعال سیستم را نشان می دهند و عبارت را می گیرند -l برای توصیف سوکت های گوش دادن و همچنین اپراتور -p برای نمایش برنامه های متصل به سوکت به عنوان مشتری. (گزینه های مفید بسیار بیشتری وجود دارد، اما این دو برای این کار کافی هستند.)

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

این نشان می دهد که شنونده فرمان است inixserver، با شناسه فرآیند 20629 اجرا می شود. (و به طور تصادفی از توصیف کننده فایل 3 به عنوان سوکت استفاده می کند.)

دومین ابزار واقعا مفید برای یافتن همان اطلاعات نام دارد lsof. تمام فایل‌های باز (یا توصیفگرهای فایل) روی سیستم را فهرست می‌کند. یا می توانید اطلاعاتی در مورد یک فایل خاص دریافت کنید:

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

Process 20629 یک سرور با عمر طولانی است، بنابراین می توانید آن را به آن متصل کنید تسمه با استفاده از چیزی شبیه به strace -o /tmp/trace -p 20629. اگر یک کار cron را در ترمینال دیگری ویرایش کنید، یک خروجی ردیابی با خطا دریافت خواهید کرد. و این هم از نتیجه:

accept(3, NULL, NULL)                   = 4
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21181
close(4)                                = 0
accept(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) = 21181
wait4(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) = 0
rt_sigreturn({mask=[]})                 = 43
accept(3, NULL, NULL)                   = 4
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21200
close(4)                                = 0
accept(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) = 21200
wait4(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) = 0
rt_sigreturn({mask=[]})                 = 43
accept(3, NULL, NULL

(آخر تایید کنید() در هنگام ردیابی تکمیل نخواهد شد.) باز هم متأسفانه، این نتیجه حاوی خطای مورد نظر ما نیست. ما هیچ پیامی را نمی بینیم که bcrontag به سوکت می فرستد یا از آن دریافت می کند. در عوض، کنترل کامل فرآیند (کلون کردن, صبر کن 4, سیگچلد و غیره) این فرآیند یک فرآیند فرزند ایجاد می کند، که، همانطور که ممکن است حدس بزنید، کار واقعی را انجام می دهد. و اگر می‌خواهید رد او را بگیرید، به تماس اضافه کنید strace -f. این همان چیزی است که وقتی پیغام خطا را در نتیجه جدید با strace جستجو می کنیم، پیدا خواهیم کرد -f -o /tmp/trace -p 20629:

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) = 36
21470 write(2, "bcron-spool[21470]: Fatal: logs:"..., 84) = 84
21470 unlink("tmp/spool.21470.1573692319.854640") = -1 ENOENT (No such file or directory)
21470 exit_group(111)                   = ?
21470 +++ exited with 111 +++

حالا، این چیزی است. هنگام تلاش برای ایجاد یک فایل در مسیر، پردازش 21470 یک خطای "دسترسی ممنوع" دریافت می کند. tmp/spool.21470.1573692319.854640 (مربوط به دایرکتوری کاری فعلی). اگر فقط دایرکتوری کاری فعلی را می دانستیم، مسیر کامل را نیز می دانستیم و می توانستیم بفهمیم که چرا فرآیند نمی تواند فایل موقت خود را در آن ایجاد کند. متأسفانه، این فرآیند قبلاً خارج شده است، بنابراین نمی توانید فقط از آن استفاده کنید lsof -p 21470 برای پیدا کردن دایرکتوری فعلی، اما می توانید در جهت مخالف کار کنید - به دنبال تماس های سیستمی PID 21470 باشید که دایرکتوری را تغییر می دهد. (اگر وجود ندارد، PID 21470 باید آنها را از والد خود به ارث برده باشد، و این قبلاً انجام شده است. lsof -p نمی توان پیدا کرد.) این تماس سیستمی است چدیر (که با کمک موتورهای جستجوی آنلاین مدرن به راحتی می توان به آن پی برد). و در اینجا نتیجه جستجوهای معکوس بر اساس نتایج ردیابی، تا سرور PID 20629 است:

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) = 36
21470 write(2, "bcron-spool[21470]: Fatal: logs:"..., 84) = 84
21470 unlink("tmp/spool.21470.1573692319.854640") = -1 ENOENT (No such file or directory)
21470 exit_group(111)                   = ?
21470 +++ exited with 111 +++

(اگر گم شده اید، ممکن است بخواهید پست قبلی من را بخوانید درباره * مدیریت فرآیند و پوسته های nix.) بنابراین، سرور PID 20629 مجوز ایجاد فایل در مسیر را دریافت نکرد /var/spool/cron/tmp/spool.21470.1573692319.854640. به احتمال زیاد، دلیل این امر تنظیمات مجوز سیستم فایل کلاسیک است. بیایید بررسی کنیم:

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

اونجا دفن سگه! سرور به‌عنوان کرون کاربر اجرا می‌شود، اما فقط root اجازه نوشتن در فهرست را دارد /var/spool/cron/tmp/. دستور ساده chown cron /var/spool/cron/tmp/ مجبور خواهد شد bcron درست کار کنند (اگر مشکل این نبود، پس محتمل ترین مظنون بعدی یک ماژول امنیتی هسته مانند SELinux یا AppArmor است، بنابراین گزارش پیام هسته را با dmesg.)

در کل

ردیابی تماس های سیستمی می تواند برای یک مبتدی بسیار طاقت فرسا باشد، اما امیدوارم نشان داده باشم که آنها راهی سریع برای اشکال زدایی یک کلاس کامل از مشکلات رایج استقرار هستند. تصور کنید سعی می کنید یک چند فرآیندی را اشکال زدایی کنید bcronبا استفاده از دیباگر گام به گام

تجزیه نتایج ردیابی به عقب در طول زنجیره فراخوانی سیستم نیاز به مهارت دارد، اما همانطور که گفتم، تقریباً همیشه، استفاده از تسمه، من فقط نتیجه ردیابی را دریافت می کنم و از انتها به دنبال خطا می گردم. به هر حال، تسمه به من کمک می کند تا زمان زیادی را در رفع اشکال صرفه جویی کنم. امیدوارم برای شما هم مفید باشد.

منبع: www.habr.com

اضافه کردن نظر