Strace ile yazılım dağıtımında hata ayıklama

Strace ile yazılım dağıtımında hata ayıklama

Günlük işim çoğunlukla yazılım dağıtımıdır; bu, aşağıdaki gibi soruları yanıtlamaya çok zaman harcadığım anlamına gelir:

  • Bu yazılım geliştirici için çalışıyor, ancak benim için değil. Neden?
  • Dün bu yazılım benim için çalıştı ama bugün çalışmıyor. Neden?

Bu, normal yazılım hata ayıklamasından biraz farklı olan bir tür hata ayıklamadır. Düzenli hata ayıklama, kodun mantığıyla ilgilidir, ancak dağıtım hata ayıklaması, kod ile ortam arasındaki etkileşimle ilgilidir. Sorunun kökünde bir mantık hatası olsa bile her şeyin bir makinede çalışıp diğerinde çalışmaması, sorunun bir şekilde ortamdan kaynaklandığı anlamına gelir.

Yani her zamanki gibi hata ayıklama araçları yerine gdb Dağıtımda hata ayıklamak için farklı bir araç setim var. Ve "Bu yazılım neden benim işime çalışmıyor?" gibi problemlerle baş etmek için en sevdiğim araç. isminde iz.

strace nedir?

iz “sistem çağrısı izleme” için bir araçtır. Başlangıçta Linux için oluşturuldu, ancak aynı hata ayıklama hileleri diğer sistemlere yönelik araçlarla da yapılabilir (DTrace veya ktrasyon).

Temel uygulama çok basittir. Strace'i herhangi bir komutla çalıştırmanız yeterlidir ve tüm sistem çağrılarını boşaltacaktır (ancak ilk önce muhtemelen onu kendiniz yüklemeniz gerekecektir) iz):

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

Bu sistem çağrıları nelerdir? Bu, işletim sistemi çekirdeği için bir API gibi bir şeydir. Bir zamanlar yazılımın üzerinde çalıştığı donanıma doğrudan erişimi vardı. Örneğin, ekranda bir şey göstermesi gerekiyorsa, video aygıtları için bağlantı noktalarıyla veya bellek eşlemeli kayıtlarla oynatılır. Çoklu görev yapabilen bilgisayar sistemleri popüler hale geldiğinde, çeşitli uygulamalar donanım için savaşırken kaos hüküm sürdü. Bir uygulamadaki hatalar tüm sistemi olmasa da diğerlerini çökertebilir. Daha sonra CPU'da ayrıcalık modları (veya "halka koruması") belirdi. Çekirdek en ayrıcalıklı hale geldi: Donanıma tam erişim elde etti ve sistem çağrıları aracılığıyla donanımla etkileşimde bulunmak için zaten çekirdekten erişim talep etmek zorunda olan daha az ayrıcalıklı uygulamaları ortaya çıkardı.

İkili düzeyde, bir sistem çağrısı basit bir işlev çağrısından biraz farklıdır, ancak çoğu program standart kitaplıkta bir sarmalayıcı kullanır. Onlar. POSIX C standart kütüphanesi bir işlev çağrısı içerir yazmak()sistem çağrısı için mimariye özgü tüm kodu içeren yazmak.

Strace ile yazılım dağıtımında hata ayıklama

Kısaca bir uygulama ile çevresi (bilgisayar sistemleri) arasındaki her türlü etkileşim sistem çağrıları aracılığıyla gerçekleştirilir. Bu nedenle yazılım bir makinede çalışıp diğerinde çalışmadığında sistem çağrısı izleme sonuçlarına bakmak iyi olacaktır. Daha spesifik olarak, bir sistem çağrısı izleme kullanılarak analiz edilebilecek tipik noktaların bir listesi aşağıda verilmiştir:

  • Konsol G/Ç
  • Ağ G/Ç
  • Dosya sistemi erişimi ve dosya G/Ç
  • Bir süreç iş parçacığının ömrünü yönetme
  • Düşük seviyeli bellek yönetimi
  • Belirli aygıt sürücülerine erişim

Strace ne zaman kullanılır?

Teoride, iz Kullanıcı alanındaki herhangi bir programla birlikte kullanılır çünkü kullanıcı alanındaki herhangi bir programın sistem çağrıları yapması gerekir. Derlenmiş, düşük seviyeli programlarla daha verimli çalışır, ancak çalışma zamanı ve yorumlayıcıdan kaynaklanan ek gürültüyü ortadan kaldırabilirseniz Python gibi yüksek seviyeli dillerle de çalışır.

Tüm ihtişamıyla iz bir makinede iyi çalışan, ancak aniden başka bir makinede çalışmayı durduran, dosyalar, izinler veya bazı komutları veya başka bir şeyi yürütmeye yönelik başarısız girişimler hakkında belirsiz mesajlar üreten bir yazılımın hata ayıklaması sırasında kendini gösterir... Yazık ama öyle değil sertifika doğrulama hataları gibi üst düzey sorunlarla çok iyi birleşiyor. Genellikle bu bir kombinasyon gerektirir izbazen iz ve daha üst düzey araçlar (komut satırı aracı gibi) openssl Sertifikada hata ayıklamak için).

Örnek olarak bağımsız bir sunucu kullanacağız ancak sistem çağrısı izleme genellikle daha karmaşık dağıtım platformlarında yapılabilir. Sadece doğru araçları seçmeniz gerekiyor.

Basit hata ayıklama örneği

Diyelim ki muhteşem sunucu uygulaması foo'yu çalıştırmak istiyorsunuz ve elde ettiğiniz sonuç bu:

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

Görünüşe göre yazdığınız konfigürasyon dosyasını bulamadı. Bunun nedeni bazen paket yöneticilerinin bir uygulamayı derlerken beklenen dosya konumlarını geçersiz kılmalarıdır. Ve bir dağıtım için kurulum kılavuzunu takip ederseniz, diğerinde beklediğinizden tamamen farklı dosyalar bulursunuz. Hata mesajı yapılandırma dosyasının nerede aranacağını söyleseydi sorun birkaç saniye içinde çözülebilirdi, ancak bu yapılmıyor. Peki nereye bakmalı?

Kaynak koduna erişiminiz varsa onu okuyabilir ve her şeyi öğrenebilirsiniz. İyi bir yedekleme planı, ancak en hızlı çözüm değil. Aşağıdaki gibi adım adım hata ayıklayıcıya başvurabilirsiniz gdb ve programın ne yaptığını görün, ancak özellikle çevreyle etkileşimi göstermek için tasarlanmış bir araç kullanmak çok daha etkilidir: iz.

Aviator apk iz gereksiz görünebilir, ancak iyi haber şu ki bunların çoğu güvenle göz ardı edilebilir. İzleme sonuçlarını ayrı bir dosyaya kaydetmek için -o operatörünü kullanmak genellikle faydalıdır:

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

Çıktının ilk sayfasının yaklaşık tamamı iz - Bu genellikle lansman için düşük seviyeli hazırlıktır. (Birçok çağrı mmap, mkoru, brk düşük seviyeli belleği algılamak ve dinamik kitaplıkları görüntülemek gibi şeyler için.) Aslında, çıktıda hata ayıklama sırasında iz Sondan okumak daha iyidir. Aşağıda bir meydan okuma olacak yazmakbir hata mesajı görüntüler. Yukarıya bakıyoruz ve ilk hatalı sistem çağrısını görüyoruz - çağrı açık, bu bir hata atar ENOENT (“dosya veya dizin bulunamadı”) açılmaya çalışılıyor /etc/foo/config.json. Yapılandırma dosyasının olması gereken yer burasıdır.

Bu sadece bir örnekti ama kullandığım zamanın %90'ını söyleyebilirim. iz, bundan daha zor bir şey yok. Aşağıda adım adım hata ayıklama kılavuzunun tamamı yer almaktadır:

  • Bir programdan gelen sistem hatasıyla ilgili belirsiz bir mesaj nedeniyle üzülmek
  • Programı şununla yeniden başlatın: iz
  • İzleme sonuçlarında hata mesajını bulun
  • İlk başarısız sistem çağrısına ulaşana kadar daha yükseğe çıkın

4. adımdaki sistem çağrısının neyin yanlış gittiğini ortaya çıkarması çok muhtemeldir.

ipuçları

Size daha karmaşık hata ayıklama örneğini göstermeden önce, etkili kullanım için birkaç püf noktası göstereceğim iz:

adam senin arkadaşın

Birçok *nix sisteminde, çekirdeğe yapılan sistem çağrılarının tam listesi çalıştırılarak elde edilebilir. adam sistem çağrıları. Gibi şeyler göreceksiniz brk(2)bu, çalıştırılarak daha fazla bilgi elde edilebileceği anlamına gelir adam 2 brk.

Küçük tırmık: adam 2 çatal bana kabuğun sayfasını gösteriyor çatal() в GNU libc'siçağrılarak uygulandığı ortaya çıktı klon(). Çağrı semantiği çatal kullanarak bir program yazarsanız aynı kalır çatal()ve bir izleme çalıştırın - hiçbir arama bulamıyorum çatal, onların yerine olacak klon(). Bu tür komisyonlar yalnızca kaynağı çıktıyla karşılaştırmaya başlarsanız kafanızı karıştırır iz.

Çıktıyı bir dosyaya kaydetmek için -o kullanın

iz kapsamlı çıktı üretebilir, bu nedenle izleme sonuçlarını ayrı dosyalarda depolamak genellikle faydalıdır (yukarıdaki örnekte olduğu gibi). Bu aynı zamanda program çıktısının çıktıyla karıştırılmasının önlenmesine de yardımcı olur. iz konsolda.

Daha fazla bağımsız değişken verisi görüntülemek için -s kullanın

Yukarıdaki örnek izlemede hata mesajının ikinci yarısının gösterilmediğini fark etmiş olabilirsiniz. Çünkü iz varsayılan dize bağımsız değişkeninin yalnızca ilk 32 baytını gösterir. Daha fazlasını görmek istiyorsanız şunun gibi bir şey ekleyin: - 128 aramaya iz.

-y dosyaları, soketleri vb. izlemeyi kolaylaştırır.

"Hepsi dosyadır", *nix sistemlerinin bir dosyaya, bir ağa veya işlemler arası kanallara uygulanıp uygulanmadığına bakılmaksızın tüm G/Ç'yi dosya tanımlayıcılarını kullanarak yaptığı anlamına gelir. Bu, programlama için uygundur ancak ortak gördüğünüzde gerçekte neler olup bittiğini takip etmeyi zorlaştırır. okumak и yazmak sistem çağrısı izleme sonuçlarında.

Operatör ekleyerek y, zorlayacaksın iz Çıktıdaki her dosya tanımlayıcıya, neyi işaret ettiğini belirten bir not ekleyin.

Zaten çalışan bir işleme -p** ile ekleme

Aşağıdaki örnekte göreceğiniz gibi bazen çalışmakta olan bir programın izini sürmeniz gerekebilir. İşlem 1337 olarak çalıştığı biliniyorsa (örneğin, çıktıdan) ps), o zaman şu şekilde izleyebilirsiniz:

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

Kök haklarına ihtiyacınız olabilir.

Alt süreçleri izlemek için -f kullanın

iz Varsayılan olarak yalnızca bir işlemi izler. Bu süreç alt süreçleri doğurursa, alt süreci oluşturmak için yapılan sistem çağrısı görülebilir ancak alt sürecin sistem çağrıları görüntülenmez.

Hatanın bir alt süreçte olduğunu düşünüyorsanız şu ifadeyi kullanın: -f, bu onun izlenmesini sağlayacaktır. Bunun dezavantajı, çıktının kafanızı daha da fazla karıştırmasıdır. Ne zaman iz bir işlemi veya bir iş parçacığını izler, çağrı olaylarının tek bir akışını gösterir. Aynı anda birden fazla işlemi izlediğinde, bir çağrının başlangıcının bir mesajla kesintiye uğradığını görebilirsiniz. , sonra - diğer yürütme dalları için bir dizi çağrı ve ancak o zaman - ilkinin sonu <…foocall devam ettirildi>. Veya operatörü kullanarak tüm izleme sonuçlarını farklı dosyalara bölün -f (detaylar liderlik üzerinde iz).

-e kullanarak izleri filtreleyin

Gördüğünüz gibi izlemenin sonucu, olası tüm sistem çağrılarının gerçek bir yığınıdır. Bayrak -e İzi filtreleyebilirsiniz (bkz. liderlik üzerinde iz). Başlıca avantajı, filtrelenmiş bir izlemeyi çalıştırmanın, tam bir izleme yapmaktan daha hızlı olmasıdır ve ardından grep'de. Dürüst olmak gerekirse neredeyse her zaman umursamıyorum.

Her hata kötü değildir

Basit ve yaygın bir örnek, bir programın, yürütülebilir bir dosya içeren bir dizini arayan bir kabuk gibi, aynı anda birden fazla yerde bir dosya aramasıdır:

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

"Bir hata bildirmeden önceki son başarısız istek" gibi buluşsal yöntemler, ilgili hataları bulmada iyidir. Öyle olsa bile, en sondan başlamak mantıklıdır.

C programlama eğitimleri sistem çağrılarını anlamanıza yardımcı olabilir.

C kitaplıklarına yapılan standart çağrılar sistem çağrıları değil, yalnızca ince bir yüzey katmanıdır. Yani C'de nasıl ve ne yapacağınızı en azından biraz anlarsanız, sistem çağrısı izlemenin sonuçlarını anlamanız daha kolay olacaktır. Örneğin, ağ sistemlerine yapılan çağrılarda hata ayıklamakta sorun yaşıyorsunuz, aynı klasiklere bakın Bija'nın Ağ Programlama Kılavuzu.

Daha karmaşık bir hata ayıklama örneği

Basit hata ayıklama örneğinin, çalışırken çoğunlukla uğraşmak zorunda kaldığım şeyin bir örneği olduğunu zaten söylemiştim. iz. Ancak bazen gerçek bir inceleme gerekebilir; işte burada daha gelişmiş hata ayıklamanın gerçek hayattan bir örneği var.

Bron - görev işleme zamanlayıcısı, *nix arka plan programının başka bir uygulaması cron. Sunucuya kuruludur, ancak birisi programı düzenlemeye çalıştığında şöyle olur:

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

Tamam, bu şu anlama geliyor Bron belirli bir dosyayı yazmaya çalıştı ama işe yaramadı ve nedenini kabul etmiyor. Ortaya çıkarmak iz:

# 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 +++

Sonlara doğru bir hata mesajı var yazmakama bu sefer bir şeyler farklı. İlk olarak, genellikle bundan önce meydana gelen ilgili bir sistem çağrısı hatası yoktur. İkincisi, bir yerlerde birisinin hata mesajını zaten okuduğu açıktır. Görünüşe göre asıl sorun başka bir yerde ve bcrontab sadece mesajı oynatır.

Bakarsan adam 2 oku, ilk argümanın (3) *nix'in tüm G/Ç işlemleri için kullandığı bir dosya tanımlayıcı olduğunu görebilirsiniz. Dosya tanımlayıcı 3'ün neyi temsil ettiğini nasıl öğrenebilirim? Bu özel durumda, koşabilirsiniz iz оператором y (yukarı bakın) ve size otomatik olarak söyleyecektir, ancak bunun gibi şeyleri anlamak için izleme sonuçlarının nasıl okunacağını ve ayrıştırılacağını bilmek yararlı olacaktır.

Bir dosya tanımlayıcının kaynağı birçok sistem çağrısından biri olabilir (hepsi tanımlayıcının ne için olduğuna bağlıdır - bir konsol, bir ağ soketi, dosyanın kendisi veya başka bir şey), ancak ne olursa olsun, biz 3 değerini döndürerek çağrılar yapın (yani izleme sonuçlarında "= 3" ifadesini ararız). Bu sonuçta 2 tane var: açık en üstte ve soket Ortada. açık dosyayı açar ama kapat(3) daha sonra tekrar kapandığını gösterecektir. (Rake: dosya tanımlayıcıları açılıp kapatıldığında tekrar kullanılabilir). Arama priz() uygun çünkü bu önceki sonuncusu oku ()ve bcrontab'ın soket aracılığıyla bir şeyle çalıştığı ortaya çıktı. Sonraki satır, dosya tanımlayıcının aşağıdakilerle ilişkili olduğunu gösterir: unix etki alanı soketi yol boyunca /var/run/bcron-spool.

Bu nedenle, ilgili süreci bulmamız gerekiyor. unix soketi diğer tarafta. Bu amaç için, her ikisi de sunucu dağıtımlarında hata ayıklamak için yararlı olan birkaç güzel püf noktası vardır. İlki kullanmaktır netstat veya daha yeni ss (soket durumu). Her iki komut da sistemin aktif ağ bağlantılarını gösterir ve ifadeyi alır. -l dinleme yuvalarını ve operatörü tanımlamak için -p istemci olarak sokete bağlı programları görüntülemek için. (Daha pek çok kullanışlı seçenek vardır ancak bu ikisi bu görev için yeterlidir.)

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

Bu, dinleyicinin komut olduğunu gösteriyor inix sunucusu20629 işlem kimliğiyle çalışıyor. (Ve tesadüfen yuva olarak dosya tanımlayıcı 3'ü kullanıyor.)

Aynı bilgiyi bulmak için gerçekten yararlı olan ikinci araca denir of. Sistemdeki tüm açık dosyaları (veya dosya tanımlayıcılarını) listeler. Veya belirli bir dosya hakkında bilgi alabilirsiniz:

# 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

İşlem 20629 uzun ömürlü bir sunucudur, dolayısıyla onu iz gibi bir şey kullanarak strace -o /tmp/trace -p 20629. Bir cron işini başka bir terminalde düzenlerseniz hatalı bir izleme çıktısı alırsınız. Ve işte sonuç:

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

(Son kabul etmek() izleme yapılırken tamamlanmayacaktır.) Yine maalesef bu sonuç aradığımız hatayı içermiyor. Bcrontag'ın sokete gönderdiği veya soketten aldığı hiçbir mesajı görmüyoruz. Bunun yerine, süreç kontrolünü tamamlayın (clone, wait4, SIGCHLD vb.) Bu süreç, tahmin edebileceğiniz gibi asıl işi yapan bir alt süreç doğurur. Ve eğer onun izini sürmeniz gerekiyorsa, aramaya ekleyin strace -f. Yeni sonuçtaki hata mesajını strace ile aradığımızda bulacağımız şey budur -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 +++

Bu da önemli bir şey. İşlem 21470, yolda bir dosya oluşturmaya çalışırken "erişim reddedildi" hatası alıyor tmp/spool.21470.1573692319.854640 (mevcut çalışma dizini ile ilgili). Geçerli çalışma dizinini bilseydik, tam yolu da bilirdik ve işlemin neden geçici dosyasını burada oluşturamadığını anlayabilirdik. Ne yazık ki, işlemden zaten çıkıldı, dolayısıyla şunu kullanamazsınız: lsof -p 21470 geçerli dizini bulmak için, ancak ters yönde de çalışabilirsiniz - dizini değiştiren PID 21470 sistem çağrılarını arayın. (Hiçbiri yoksa, PID 21470 bunları üst öğesinden devralmış olmalıdır ve bu zaten lsof -p bulunamıyor.) Bu sistem çağrısı chdir (Modern çevrimiçi arama motorlarının yardımıyla bunu bulmak kolaydır). Ve işte PID 20629 sunucusuna kadar izleme sonuçlarına dayalı ters aramaların sonucu:

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 +++

(Kaybolduysanız önceki yazımı okumak isteyebilirsiniz *nix süreç yönetimi ve kabuklar hakkında.) Bu nedenle, PID 20629 sunucusu yolda bir dosya oluşturma izni alamadı /var/spool/cron/tmp/spool.21470.1573692319.854640. Büyük olasılıkla bunun nedeni klasik dosya sistemi izin ayarlarıdır. Hadi kontrol edelim:

# 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

Köpeğin gömüldüğü yer orası! Sunucu bir kullanıcı cronu olarak çalışır, ancak yalnızca kökün dizine yazma izni vardır /var/spool/cron/tmp/. Basit komut chown cron /var/spool/cron/tmp/ yapacak Bron doğru çalışın. (Sorun bu değilse, bir sonraki en olası şüpheli SELinux veya AppArmor gibi bir çekirdek güvenlik modülüdür, bu yüzden çekirdek mesaj günlüğünü şununla kontrol ederdim: dmesg.)

Toplam

Sistem çağrısı izlemeleri yeni başlayanlar için bunaltıcı olabilir, ancak umarım bunların tüm yaygın dağıtım sorunlarında hata ayıklamanın hızlı bir yolu olduğunu göstermişimdir. Çoklu işlemde hata ayıklamaya çalıştığınızı hayal edin Bronadım adım hata ayıklayıcı kullanarak.

İzleme sonuçlarını sistem çağrı zinciri boyunca geriye doğru ayrıştırmak beceri gerektirir, ancak söylediğim gibi neredeyse her zaman iz, sadece izleme sonucunu alıyorum ve sondan başlayarak hataları arıyorum. Her neyse, iz hata ayıklama konusunda çok zaman kazanmama yardımcı oluyor. Umarım size de faydalı olur.

Kaynak: habr.com

Yorum ekle