كيف ينبغي استخدام ستريس؟
سؤال
أخبرني أحد الزملاء ذات مرة أن الخيار الأخير عندما فشل كل شيء في تصحيح الأخطاء على Linux هو الاستخدام Strace.
لقد حاولت أن أتعلم العلم وراء هذه الأداة الغريبة، لكنني لست خبيرًا في إدارة النظام ولم أحصل على نتائج حقًا.
لذا،
- ما هو بالضبط وماذا يفعل؟
- كيف وفي أي الحالات يجب استخدامه؟
- كيف ينبغي فهم المخرجات ومعالجتها؟
باختصار، بكلمات بسيطة, ، كيف تعمل هذه الأشياء؟
المحلول
نظرة عامة على ستريس
يمكن اعتبار Strace مصحح أخطاء خفيف الوزن.فهو يسمح للمبرمج / المستخدم باكتشاف كيفية تفاعل البرنامج مع نظام التشغيل بسرعة.يقوم بذلك عن طريق مراقبة مكالمات وإشارات النظام.
الاستخدامات
مناسب عندما لا يكون لديك كود مصدر أو لا تريد أن تزعج نفسك بمراجعته.
كما أنه مفيد للتعليمات البرمجية الخاصة بك إذا كنت لا ترغب في فتح GDB، ولكنك مهتم فقط بفهم التفاعل الخارجي.
مقدمة صغيرة جيدة
لقد واجهت هذه المقدمة لاستخدامها في اليوم الآخر فقط: ستريس مرحبا بالعالم
نصائح أخرى
بكلمات بسيطة، يتتبع strace جميع مكالمات النظام الصادرة عن البرنامج بالإضافة إلى رموز الإرجاع الخاصة بها.فكر في أشياء مثل عمليات الملفات/المقبس وغيرها الكثير من العمليات الغامضة.
يكون هذا مفيدًا للغاية إذا كان لديك بعض المعرفة العملية بلغة C نظرًا لأن مكالمات النظام هنا ستمثل بشكل أكثر دقة مكالمات مكتبة C القياسية.
لنفترض أن برنامجك هو /usr/local/bin/cough.ببساطة استخدم:
strace /usr/local/bin/cough <any required argument for cough here>
أو
strace -o <out_file> /usr/local/bin/cough <any required argument for cough here>
للكتابة في "out_file".
سيتم نقل كل مخرجات التتبع إلى stderr (احذر، فغالبًا ما يطلب الحجم الهائل منه إعادة التوجيه إلى ملف).في أبسط الحالات، سيتم إيقاف برنامجك بسبب حدوث خطأ، وستكون قادرًا على معرفة مكان تفاعلاته الأخيرة مع نظام التشغيل في إخراج التتبع.
مزيد من المعلومات ينبغي أن تكون متاحة مع:
man strace
يسرد strace جميع مكالمات النظام يتم ذلك من خلال العملية التي يتم تطبيقها عليها.إذا كنت لا تعرف ما تعنيه مكالمات النظام، فلن تتمكن من الحصول على الكثير من الأميال منه.
ومع ذلك، إذا كانت مشكلتك تتضمن ملفات أو مسارات أو قيم بيئة، فإن تشغيل strace على البرنامج الذي به المشكلة وإعادة توجيه الإخراج إلى ملف ثم التقاط هذا الملف لسلسلة المسار/الملف/env قد يساعدك في معرفة ما هو برنامجك في الحقيقة تحاول القيام به، على عكس ما كنت تتوقعه.
تبرز Strace كأداة للتحقيق في أنظمة الإنتاج حيث لا يمكنك تشغيل هذه البرامج تحت مصحح الأخطاء.على وجه الخصوص، استخدمنا strace في الحالتين التاليتين:
- يبدو أن برنامج foo في طريق مسدود ولم يعد يستجيب.قد يكون هذا هدفًا لـ gdb؛ومع ذلك، لم يكن لدينا دائمًا كود المصدر أو كنا نتعامل في بعض الأحيان مع لغات مكتوبة لم يكن من السهل تشغيلها تحت مصحح الأخطاء.في هذه الحالة، تقوم بتشغيل strace على برنامج قيد التشغيل بالفعل وستحصل على قائمة باستدعاءات النظام التي يتم إجراؤها.يعد هذا مفيدًا بشكل خاص إذا كنت تقوم بالتحقق من تطبيق عميل/خادم أو تطبيق يتفاعل مع قاعدة بيانات
- التحقيق في سبب بطء البرنامج.وعلى وجه الخصوص، كنا قد انتقلنا للتو إلى نظام ملفات موزع جديد وكان معدل النقل الجديد للنظام بطيئًا للغاية.يمكنك تحديد strace باستخدام خيار "-T" الذي سيخبرك بمقدار الوقت المستغرق في كل استدعاء للنظام.ساعد هذا في تحديد سبب تسبب نظام الملفات في إبطاء الأمور.
للحصول على مثال للتحليل باستخدام strace، راجع إجابتي على هذا السؤال.
أستخدم strace طوال الوقت لتصحيح مشكلات الأذونات.التقنية تسير على النحو التالي:
$ strace -e trace=open,stat,read,write gnome-calculator
أين gnome-calculator
هو الأمر الذي تريد تشغيله.
strace -tfp PID سوف يراقب مكالمات نظام عملية PID، وبالتالي يمكننا تصحيح/مراقبة حالة العملية/البرنامج.
يمكن استخدام Strace كأداة تصحيح أو كملف تعريف بدائي.
باعتبارك مصحح أخطاء، يمكنك رؤية كيفية استدعاء مكالمات النظام المحددة وتنفيذها وماذا تعود.هذا مهم جدًا، لأنه يسمح لك برؤية ليس فقط فشل البرنامج، ولكن أيضًا سبب فشل البرنامج.عادةً ما يكون ذلك مجرد نتيجة للترميز الرديء الذي لا يلتقط جميع النتائج المحتملة للبرنامج.وفي أحيان أخرى تكون مجرد مسارات مشفرة للملفات.بدون أثر، يمكنك تخمين الخطأ الذي حدث وأين وكيف.مع strace تحصل على تفاصيل استدعاء النظام، وعادةً ما يخبرك مجرد النظر إلى القيمة المرجعة بالكثير.
التنميط هو استخدام آخر.يمكنك استخدامه لتحديد وقت تنفيذ كل مكالمات النظام بشكل فردي، أو بشكل إجمالي.في حين أن هذا قد لا يكون كافيًا لإصلاح مشكلاتك، إلا أنه على الأقل سيؤدي إلى تضييق نطاق قائمة المشتبه بهم المحتملين بشكل كبير.إذا رأيت الكثير من أزواج الفتح/الإغلاق في ملف واحد، فمن المحتمل أنك تفتح الملفات وتغلقها بشكل غير ضروري في كل تنفيذ للحلقة، بدلاً من فتحها وإغلاقها خارج الحلقة.
Ltrace هو ابن عم ستريس المقرب، وهو أيضًا مفيد جدًا.يجب أن تتعلم كيفية التمييز بين مكان عنق الزجاجة الخاص بك.إذا كان إجمالي التنفيذ 8 ثوانٍ، وقضيت 0.05 ثانية فقط على مكالمات النظام، فلن يفيدك تعقب البرنامج كثيرًا، فالمشكلة تكمن في التعليمات البرمجية الخاصة بك، والتي عادةً ما تكون مشكلة منطقية، أو أن البرنامج يحتاج بالفعل إلى ليأخذ هذا الوقت الطويل للتشغيل.
أكبر مشكلة في strace/ltrace هي قراءة مخرجاتها.إذا كنت لا تعرف كيفية إجراء المكالمات، أو على الأقل أسماء syscalls/الوظائف، فسيكون من الصعب فك المعنى.يمكن أن تكون معرفة ما ترجعه الوظائف مفيدًا جدًا أيضًا، خاصة بالنسبة لرموز الأخطاء المختلفة.على الرغم من صعوبة فك الشفرات، إلا أنها في بعض الأحيان تُرجع لؤلؤة من المعرفة؛بمجرد أن رأيت موقفًا نفدت فيه inodes، ولكن ليس نفدت المساحة الحرة، وبالتالي لم تعطني جميع الأدوات المساعدة المعتادة أي تحذير، ولم أتمكن من إنشاء ملف جديد.لقد وجهتني قراءة رمز الخطأ من مخرجات strace في الاتجاه الصحيح.
Strace هي أداة تخبرك بكيفية تفاعل تطبيقك مع نظام التشغيل الخاص بك.
يقوم بذلك عن طريق إخبارك بنظام التشغيل الذي يستدعيه تطبيقك والمعلمات التي يستدعيها.
لذلك، على سبيل المثال، ترى الملفات التي يحاول برنامجك فتحها، وحالة نجاح المكالمة.
يمكنك تصحيح جميع أنواع المشاكل باستخدام هذه الأداة.على سبيل المثال، إذا قال التطبيق أنه لا يمكنه العثور على المكتبة التي تعلم أنك قمت بتثبيتها، فسوف يخبرك strace بالمكان الذي يبحث فيه التطبيق عن هذا الملف.
وهذا مجرد غيض من فيض.
يعد strace أداة جيدة لتعلم كيفية إجراء برنامجك لاستدعاءات النظام المختلفة (الطلبات إلى النواة) وكذلك الإبلاغ عن تلك التي فشلت بالإضافة إلى قيمة الخطأ المرتبطة بهذا الفشل.ليست كل حالات الفشل عبارة عن أخطاء.على سبيل المثال، قد يحصل التعليمة البرمجية التي تحاول البحث عن ملف على خطأ ENOENT (لا يوجد مثل هذا الملف أو الدليل) ولكن قد يكون هذا سيناريو مقبولًا في منطق التعليمات البرمجية.
إحدى حالات الاستخدام الجيد لاستخدام strace هي تصحيح حالات السباق أثناء إنشاء الملف المؤقت.على سبيل المثال، قد يواجه البرنامج الذي قد يقوم بإنشاء ملفات عن طريق إلحاق معرف العملية (PID) ببعض السلاسل المحددة مسبقًا مشكلات في السيناريوهات متعددة الخيوط.[PID+TID (معرف العملية + معرف مؤشر الترابط) أو استدعاء نظام أفضل مثل mkstemp سيصلح هذه المشكلة].
كما أنها جيدة لتصحيح الأعطال.قد تجد هذه المقالة (الخاصة بي) حول أعطال التتبع وتصحيح الأخطاء مفيد.
أعجبني بعض الإجابات حيث يقرأ strace
يتحقق من كيفية تفاعلك مع نظام التشغيل الخاص بك.
وهذا هو بالضبط ما يمكننا رؤيته.يدعو النظام.إذا قارنت strace
و ltrace
الفرق أكثر وضوحا.
$>strace -c cd
Desktop Documents Downloads examples.desktop Music Pictures Public Templates Videos
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
0.00 0.000000 0 7 read
0.00 0.000000 0 1 write
0.00 0.000000 0 11 close
0.00 0.000000 0 10 fstat
0.00 0.000000 0 17 mmap
0.00 0.000000 0 12 mprotect
0.00 0.000000 0 1 munmap
0.00 0.000000 0 3 brk
0.00 0.000000 0 2 rt_sigaction
0.00 0.000000 0 1 rt_sigprocmask
0.00 0.000000 0 2 ioctl
0.00 0.000000 0 8 8 access
0.00 0.000000 0 1 execve
0.00 0.000000 0 2 getdents
0.00 0.000000 0 2 2 statfs
0.00 0.000000 0 1 arch_prctl
0.00 0.000000 0 1 set_tid_address
0.00 0.000000 0 9 openat
0.00 0.000000 0 1 set_robust_list
0.00 0.000000 0 1 prlimit64
------ ----------- ----------- --------- --------- ----------------
100.00 0.000000 93 10 total
ومن ناحية أخرى هناك ltrace
الذي يتتبع الوظائف.
$>ltrace -c cd
Desktop Documents Downloads examples.desktop Music Pictures Public Templates Videos
% time seconds usecs/call calls function
------ ----------- ----------- --------- --------------------
15.52 0.004946 329 15 memcpy
13.34 0.004249 94 45 __ctype_get_mb_cur_max
12.87 0.004099 2049 2 fclose
12.12 0.003861 83 46 strlen
10.96 0.003491 109 32 __errno_location
10.37 0.003303 117 28 readdir
8.41 0.002679 133 20 strcoll
5.62 0.001791 111 16 __overflow
3.24 0.001032 114 9 fwrite_unlocked
1.26 0.000400 100 4 __freading
1.17 0.000372 41 9 getenv
0.70 0.000222 111 2 fflush
0.67 0.000214 107 2 __fpending
0.64 0.000203 101 2 fileno
0.62 0.000196 196 1 closedir
0.43 0.000138 138 1 setlocale
0.36 0.000114 114 1 _setjmp
0.31 0.000098 98 1 realloc
0.25 0.000080 80 1 bindtextdomain
0.21 0.000068 68 1 opendir
0.19 0.000062 62 1 strrchr
0.18 0.000056 56 1 isatty
0.16 0.000051 51 1 ioctl
0.15 0.000047 47 1 getopt_long
0.14 0.000045 45 1 textdomain
0.13 0.000042 42 1 __cxa_atexit
------ ----------- ----------- --------- --------------------
100.00 0.031859 244 total
على الرغم من أنني راجعت الأدلة عدة مرات، إلا أنني لم أجد أصل الاسم strace
ولكن من المحتمل أن يكون تتبع استدعاء النظام، لأن هذا واضح.
هناك ثلاث ملاحظات أكبر لنقولها strace
.
ملاحظة 1:كل من هذه الوظائف strace
و ltrace
يستخدمون مكالمة النظام ptrace
.لذا ptrace
استدعاء النظام هو على نحو فعال كيف strace
يعمل.
The ptrace() system call provides a means by which one process (the "tracer") may observe and control the execution of another process (the "tracee"), and examine and change the tracee's memory and registers. It is primarily used to implement breakpoint debugging and system call tracing.
ملاحظة 2:هناك معلمات مختلفة يمكنك استخدامها معها strace
, ، منذ strace
يمكن أن يكون مطولاً جداً.أحب التجربة -c
وهو بمثابة ملخص للأشياء.مرتكز على -c
يمكنك تحديد مكالمة نظام واحدة مثل -e trace=open
حيث سترى تلك المكالمة فقط.قد يكون هذا مثيرًا للاهتمام إذا كنت تفحص الملفات التي سيتم فتحها أثناء الأمر الذي تقوم بتتبعه.وبالطبع، يمكنك استخدام grep
لنفس الغرض ولكن لاحظ أنك بحاجة إلى إعادة التوجيه بهذه الطريقة 2>&1 | grep etc
لفهم أن ملفات التكوين يتم الرجوع إليها عند إصدار الأمر.
ملاحظة 3:أجد هذه الملاحظة مهمة جدًا.أنت لا تقتصر على بنية معينة. strace
سوف يذهلك، لأنه يمكن أن يتتبع ثنائيات من بنيات مختلفة.
الحد الأدنى من الأمثلة القابلة للتشغيل
إذا لم يكن المفهوم واضحًا، فهناك مثال أبسط لم تره يشرحه.
في هذه الحالة، هذا المثال هو تجميع Linux x86_64 القائم بذاته (بدون libc) helloworld:
مرحبا.س
.text
.global _start
_start:
/* write */
mov $1, %rax /* syscall number */
mov $1, %rdi /* stdout */
mov $msg, %rsi /* buffer */
mov $len, %rdx /* buffer len */
syscall
/* exit */
mov $60, %rax /* exit status */
mov $0, %rdi /* syscall number */
syscall
msg:
.ascii "hello\n"
len = . - msg
تجميع وتشغيل:
as -o hello.o hello.S
ld -o hello.out hello.o
./hello.out
المخرجات المتوقعة:
hello
الآن دعونا نستخدم strace في هذا المثال:
env -i ASDF=qwer strace -o strace.log -s999 -v ./hello.out arg0 arg1
cat strace.log
نحن نستخدم:
env -i ASDF=qwer
للتحكم في متغيرات البيئة: https://unix.stackexchange.com/questions/48994/how-to-run-a-program-in-a-clean-environment-in-bash-s999 -v
لإظهار معلومات أكمل على السجلات
strace.log
يحتوي الآن على:
execve("./hello.out", ["./hello.out", "arg0", "arg1"], ["ASDF=qwer"]) = 0
write(1, "hello\n", 6) = 6
exit(0) = ?
+++ exited with 0 +++
مع هذا المثال البسيط، كل حرف من الناتج يكون واضحًا بذاته:
execve
خط:يظهر كيفstrace
أعدمhello.out
, ، بما في ذلك وسيطات CLI والبيئة كما هو موثق فيman execve
write
خط:يُظهر استدعاء نظام الكتابة الذي قمنا به.6
هو طول السلسلة"hello\n"
.= 6
هي القيمة المرجعة لاستدعاء النظام، كما هو موثق فيman 2 write
هو عدد البايتات المكتوبة.exit
خط:يُظهر مكالمة نظام الخروج التي قمنا بها.لا توجد قيمة إرجاع، منذ توقف البرنامج!
أمثلة أكثر تعقيدا
إن تطبيق strace هو بالطبع معرفة النظام الذي يستدعي البرامج المعقدة التي تقوم بها بالفعل للمساعدة في تصحيح أخطاء برنامجك/تحسينه.
والجدير بالذكر أن معظم مكالمات النظام التي من المحتمل أن تواجهها في Linux تحتوي على أغلفة glibc، العديد منهم من POSIX.
داخليًا، تستخدم مغلفات glibc التجميع المضمّن كما يلي تقريبًا: كيفية استدعاء استدعاء النظام عبر sysenter في التجميع المضمن؟
المثال التالي الذي يجب عليك دراسته هو POSIX write
مرحبا بالعالم:
ج الرئيسية
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *msg = "hello\n";
write(1, msg, 6);
return 0;
}
تجميع وتشغيل:
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out
هذه المرة، سترى أنه تم إجراء مجموعة من مكالمات النظام بواسطة glibc من قبل main
لإعداد بيئة جميلة لـ main.
وذلك لأننا لا نستخدم الآن برنامجًا قائمًا بذاته، بل نستخدم برنامج glibc الأكثر شيوعًا، والذي يسمح بوظيفة libc.
ثم في كل نهاية، strace.log
يتضمن:
write(1, "hello\n", 6) = 6
exit_group(0) = ?
+++ exited with 0 +++
لذلك نستنتج أن write
تستخدم وظيفة POSIX، مفاجأة!، Linux write
مكالمة النظام.
ونحن نلاحظ ذلك أيضا return 0
يؤدي إلى exit_group
اتصل بدلا من exit
.هههه لم أكن أعلم بهذا الأمر!هذا هو السبب strace
بارد جدا. man exit_group
ثم يشرح:
استدعاء النظام هذا يعادل الخروج (2) فيما عدا أنه لا ينهي مؤشر ترابط الاستدعاء فحسب، بل جميع سلاسل الرسائل الموجودة في مجموعة مؤشرات ترابط عملية الاستدعاء.
وهنا مثال آخر حيث قمت بدراسة استدعاء النظام dlopen
الاستخدامات: https://unix.stackexchange.com/questions/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710
تم الاختبار في Ubuntu 16.04 وGCC 6.4.0 وLinux kernel 4.4.0.