Tersine hata ayıklama nasıl çalışır?


82

GDB'nin ters hata ayıklamayı destekleyen yeni bir sürümü var (bkz. Http://www.gnu.org/software/gdb/news/reversible.html ). Nasıl çalıştığını merak etmeliyim.

Tersine hata ayıklamanın çalışması için bana öyle geliyor ki, her adım için bellek dahil tüm makine durumunu saklamanız gerekiyor. Bu, performansı inanılmaz derecede yavaşlatır, çok fazla bellek kullanmaktan bahsetmiyorum bile. Bu sorunlar nasıl çözülür?


4
Eyaletin tamamı yerine eyalet deltalarını depolamakla geçinebileceğinizi hayal ediyorum, ancak yine de maliyetli gibi görünüyor.
harcayan


Deltaların kaydedilmesi gerçekten çok iyi çalışabilir ve verimli bir tam sistem geri dönüşümlü çözüm için gerçekten gereklidir.
jakobengblom2

Yanıtlar:


131

Ben bir gdb geliştiricisiyim ve yeni ters hata ayıklamanın yazarlarından biriyim. Nasıl çalıştığı hakkında konuşmaktan mutluluk duyarım. Birkaç kişinin tahmin ettiği gibi, daha sonra geri yükleyebileceğiniz kadar makine durumunu kaydetmeniz gerekir. Bir dizi şema vardır, bunlardan biri her bir makine talimatı tarafından değiştirilen kayıtları veya bellek konumlarını basitçe kaydetmek içindir. Ardından, bu talimatı "geri almak" için, bu yazmaçlardaki veya bellek konumlarındaki verileri geri döndürmeniz yeterlidir.

Evet, pahalıdır, ancak modern cpus o kadar hızlıdır ki, yine de etkileşimli olduğunuzda (adım atma veya kesme noktaları yaparak), gerçekten o kadar fark etmezsiniz.


4
Ancak ters hata ayıklama, yalnızca yazdığınız komutları nextve stepkomutları geri almanıza izin veriyor mu, yoksa herhangi bir sayıda talimatı geri almanıza izin veriyor mu? Örneğin, bir talimat için bir kesme noktası ayarlarsam ve o zamana kadar çalışmasına izin verirsem, bunu atlamış olsam bile önceki talimata geri dönebilir miyim?
Nathan Fellman

10
> Ancak ters hata ayıklama, yalnızca bir sonraki ve yazdığınız komutları geri almanıza izin verir veya herhangi bir sayıda talimatı geri almanıza izin verir Herhangi bir sayıda talimatı geri alabilirsiniz. Sınırlı değilsiniz, örneğin sadece ileriye giderken durduğunuz noktalarda durmak. Yeni bir kesme noktası ayarlayabilir ve geriye doğru koşabilirsiniz> Örneğin, bir talimat için bir kesme noktası ayarlayıp o zamana kadar çalışmasına izin verirsem, daha sonra atlamış olsam bile önceki talimata geri dönebilir miyim Evet kesme noktasına koşmadan önce kayıt modunu açtım
Michael Snyder

3
Biçimlendirilmemiş metin için üzgünüm, bunun ne olduğunu bilmiyorum.
Michael Snyder

10
Tersine hata ayıklamanın zamanı geri alabileceğinden ve bizi 60'lara veya 70'lere geri götüreceğinden endişeleniyorum. Çan pantolon giyip saçımı tekrar uzatmak istemiyorum.
The Tin Man

3
Ve işletim sistemindeki durumu değiştiren sistem çağrıları? Bu düzgün çalışmıyor mu? Opak bir tutamacı değiştirdiğinde ne olur?
Adrian

12

Ters yürütmeyi uygulamak için simülatörlerin, sanal makinelerin ve donanım kaydedicilerin kullanımını unutmamanız gerektiğini unutmayın.

Uygulamanın başka bir çözümü de donanım tabanlı hata ayıklayıcılarında GreenHills ve Lauterbach tarafından yapıldığı gibi fiziksel donanım üzerindeki yürütmeyi izlemektir. Her bir talimatın eyleminin bu sabit izine dayanarak, sırayla her talimatın etkilerini kaldırarak izdeki herhangi bir noktaya gidebilirsiniz. Bunun, hata ayıklayıcıda görünen durumu etkileyen her şeyi izleyebileceğinizi varsaydığını unutmayın.

Diğer bir yol, VmWare Workstation 6.5 ve Virtutech Simics 3.0 (ve üstü) tarafından kullanılan ve Visual Studio 2010 ile birlikte geliyor gibi görünen bir kontrol noktası + yeniden yürütme yöntemi kullanmaktır. Burada, bir sanal makine veya bir simülatör kullanıyorsunuz. bir sistemin yürütülmesi konusunda bir dolaylılık düzeyi elde etmek. Düzenli olarak tüm durumu diske veya belleğe atarsınız ve ardından simülatörün aynı program yolunu belirleyici olarak yeniden çalıştırabileceğine güvenirsiniz.

Basitleştirilmiş, şu şekilde çalışır: bir sistemin yürütülmesinde T zamanında olduğunuzu söyleyin. T-1 zamanına gitmek için, t <T noktasından bir kontrol noktası alırsınız ve daha sonra bulunduğunuz yerden bir döngü önce bitirmek için (Tt-1) döngüleri yürütürsünüz. Bu, çok iyi çalışması için yapılabilir ve disk GÇ'si yapan, çekirdek düzeyinde koddan oluşan ve aygıt sürücüsü çalışmasını gerçekleştiren iş yükleri için bile geçerlidir. Anahtar, tüm işlemcileri, cihazları, bellekleri ve IO'ları ile tüm hedef sistemi içeren bir simülatöre sahip olmaktır. Bkz gdb mailinglist ve daha fazla ayrıntı için gdb posta listesinde olduğu aşağıdaki tartışmayı. Bu yaklaşımı, özellikle aygıt sürücülerinde ve erken işletim sistemi önyüklemelerinde zor kodlarda hata ayıklamak için oldukça düzenli olarak kullanıyorum.

Başka bir bilgi kaynağı da kontrol noktası belirleme üzerine bir Virtutech teknik raporu (tam açıklamayla yazdığım).


Ayrıca , ters hata ayıklama tekniklerinin daha kapsamlı bir incelemesi için jakob.engbloms.se/archives/1547 ve aşağıdaki iki blog gönderisine bakın .
jakobengblom2

Ters adım atmak yerine "kaydetme noktaları belirleme" becerisine ne dersiniz? Böylece, hata ayıklama yaparsınız ve bir noktada mevcut adımı "kaydetme noktası" olarak seçebilir ve daha sonra bu kaydetme noktasına geri dönebilir ve gerekirse değişkenlerinizi düzenleyerek tekrar ileri adım atma olanağına sahip olursunuz. Sanal makineler için "anlık görüntüler" veya işletim sistemleri için "geri yükleme noktaları" gibi.
Rolf

9

EclipseCon oturumu sırasında, bunu Java için Chronon Debugger ile nasıl yaptıklarını da sorduk . Bu, gerçekte geri adım atmanıza izin vermez , ancak kaydedilmiş bir programı tersine hata ayıklama gibi hissettirecek şekilde oynatabilir . (Temel fark, çalışan programı Chronon hata ayıklayıcısında değiştiremezken, bunu diğer Java hata ayıklayıcılarının çoğunda yapabilirsiniz.)

Doğru anladıysam, çalışan programın bayt kodunu değiştirir, böylece programın dahili durumundaki her değişiklik kaydedilir. Dış durumların ek olarak kaydedilmesine gerek yoktur. Programınızı bir şekilde etkiliyorlarsa, bu harici durumla eşleşen dahili bir değişkene sahip olmalısınız (ve bu nedenle bu dahili değişken yeterlidir).

Oynatma süresi sırasında, temelde çalışan programın her durumunu kayıtlı durum değişikliklerinden yeniden oluşturabilirler.

İlginçtir ki, durum değişiklikleri ilk bakışta beklenenden çok daha küçüktür. Öyleyse, koşullu bir "eğer" ifadeniz varsa, programın o zaman veya başka ifadeyi alıp almadığını kaydetmek için en az bir bit'e ihtiyacınız olduğunu düşünürsünüz. Çoğu durumda, bu farklı dalların bir dönüş değeri içermesi durumunda olduğu gibi bundan bile kaçınabilirsiniz. O zaman sadece dönüş değerini (yine de gerekli olacak) kaydetmek ve çalıştırılan şubeyle ilgili kararı dönüş değerinin kendisinden yeniden hesaplamak yeterlidir .


8

Bu soru eski olmasına rağmen, cevapların çoğu da ve ilginç bir konu olmaya devam ediyor, 2015 cevabı gönderiyorum. Yüksek Lisans tezimin 1. ve 2. bölümleri, Ters hata ayıklama ve canlı programlamayı bilgisayar programlamada görsel düşünmeye doğru birleştirmek , hata ayıklamayı tersine çevirmek için bazı tarihsel yaklaşımları kapsar (özellikle anlık görüntü (veya kontrol noktası) ve tekrar yaklaşımı üzerine odaklanır) ve onunla her şeyi bilen hata ayıklama arasındaki farkı açıklıyor:

Programı bir noktaya kadar ileriye taşıyan bilgisayar, bize gerçekten bu konuda bilgi verebilmelidir. Böyle bir gelişme mümkündür ve her şeyi bilen hata ayıklayıcılar olarak adlandırılan şeylerde bulunur. Genellikle ters hata ayıklayıcılar olarak sınıflandırılırlar, ancak programcının bir yürütme programında zamanda geriye doğru adım atmasına izin vermek yerine, daha sonra görüntülemek veya sorgulamak için yalnızca yürütme sırasında bilgileri kaydettikleri için "geçmiş günlüğü" hata ayıklayıcıları olarak daha doğru bir şekilde tanımlanabilirler. . "Omniscient", programın kaydedilmiş olan tüm durum geçmişinin çalıştırıldıktan sonra hata ayıklayıcı tarafından kullanılabilir olmasından gelir. Bu durumda programı yeniden çalıştırmaya ve manuel kod enstrümantasyonuna gerek kalmaz.

Yazılım tabanlı her şeyi bilen hata ayıklama, "hata ayıklama zamanı geçmiş oynatma" olarak adlandırılan 1969 EXDAMS sistemiyle başladı. GNU hata ayıklayıcı GDB, 2009'dan beri "işlem kaydı ve tekrar" özelliği ile her şeyi bilen hata ayıklamayı desteklemektedir. TotalView, UndoDB ve Chronon şu anda mevcut olan en iyi her şeyi bilen hata ayıklayıcılar gibi görünüyor, ancak bunlar ticari sistemlerdir. Java için TOD, kısmi deterministik tekrarın yanı sıra kısmi izleme yakalama ve ilgili büyük hacimli bilgilerin kaydedilmesini sağlamak için dağıtılmış bir veritabanı kullanan en iyi açık kaynak alternatifi gibi görünüyor.

Yalnızca bir kaydın gezinmesine izin vermeyen, aynı zamanda yürütme zamanında geri adım atabilen hata ayıklayıcılar da mevcuttur. Daha doğru bir şekilde zamanda geri dönüş, zamanda yolculuk, çift yönlü veya ters hata ayıklayıcılar olarak tanımlanabilirler.

Bu tür ilk sistem 1981 COPE prototipiydi ...


4

mozilla rr, GDB tersine hata ayıklamaya daha sağlam bir alternatiftir

https://github.com/mozilla/rr

GDB'nin yerleşik kaydı ve yeniden oynatması ciddi sınırlamalara sahiptir, örneğin AVX talimatları için destek yoktur: gdb ters hata ayıklama, "İşlem kaydı adreste 0xf0d talimatını desteklemiyor" ile başarısız olur

Rr'nin avantajları:

  • şu anda çok daha güvenilir. Birkaç karmaşık yazılımın nispeten uzun sürelerini test ettim.
  • ayrıca gdbserver protokolüne sahip bir GDB arayüzü sunar, bu da onu harika bir yedek haline getirir
  • çoğu program için küçük bir performans düşüşü, ölçümler yapmadan bunu kendim fark etmedim
  • oluşturulan izler diskte küçüktür çünkü yalnızca çok az deterministik olmayan olay kaydedilir, şimdiye kadar boyutları hakkında endişelenmek zorunda kalmadım

rr bunu, önce programı bir iş parçacığı anahtarı gibi deterministik olmayan her olayda olanları kaydedecek şekilde çalıştırarak başarır.

Daha sonra ikinci tekrar çalıştırma sırasında, orijinal deterministik olmayan çalışmada tam olarak ne olduğunu ancak ileriye veya geriye doğru belirleyici bir şekilde yeniden yapılandırmak için şaşırtıcı derecede küçük olan bu izleme dosyasını kullanır.

rr, ilk olarak Mozilla tarafından, ertesi gün gece testlerinde ortaya çıkan zamanlama hatalarını yeniden üretmelerine yardımcı olmak için geliştirildi. Ancak ters hata ayıklama yönü, yalnızca çalıştırma içinde saatler süren bir hataya sahip olduğunuzda da önemlidir, çünkü önceki durumun sonraki başarısızlığa yol açtığını incelemek için genellikle geri adım atmak istersiniz.

Aşağıdaki örnek bazı özellikleri, özellikle sergiliyor reverse-next, reverse-stepve reverse-continuekomutları.

Ubuntu 18.04'e yükleyin:

sudo apt-get install rr linux-tools-common linux-tools-generic linux-cloud-tools-generic
sudo cpupower frequency-set -g performance
# Overcome "rr needs /proc/sys/kernel/perf_event_paranoid <= 1, but it is 3."
echo 'kernel.perf_event_paranoid=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Test programı:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int f() {
    int i;
    i = 0;
    i = 1;
    i = 2;
    return i;
}

int main(void) {
    int i;

    i = 0;
    i = 1;
    i = 2;

    /* Local call. */
    f();

    printf("i = %d\n", i);

    /* Is randomness completely removed?
     * Recently fixed: https://github.com/mozilla/rr/issues/2088 */
    i = time(NULL);
    printf("time(NULL) = %d\n", i);

    return EXIT_SUCCESS;
}

derleyin ve çalıştırın:

gcc -O0 -ggdb3 -o reverse.out -std=c89 -Wextra reverse.c
rr record ./reverse.out
rr replay

Artık bir GDB oturumunun içinde kaldınız ve hata ayıklamayı düzgün bir şekilde tersine çevirebilirsiniz:

(rr) break main
Breakpoint 1 at 0x55da250e96b0: file a.c, line 16.
(rr) continue
Continuing.

Breakpoint 1, main () at a.c:16
16          i = 0;
(rr) next
17          i = 1;
(rr) print i
$1 = 0
(rr) next
18          i = 2;
(rr) print i
$2 = 1
(rr) reverse-next
17          i = 1;
(rr) print i
$3 = 0
(rr) next
18          i = 2;
(rr) print i
$4 = 1
(rr) next
21          f();
(rr) step
f () at a.c:7
7           i = 0;
(rr) reverse-step
main () at a.c:21
21          f();
(rr) next
23          printf("i = %d\n", i);
(rr) next
i = 2
27          i = time(NULL);
(rr) reverse-next
23          printf("i = %d\n", i);
(rr) next
i = 2
27          i = time(NULL);
(rr) next
28          printf("time(NULL) = %d\n", i);
(rr) print i
$5 = 1509245372
(rr) reverse-next
27          i = time(NULL);
(rr) next
28          printf("time(NULL) = %d\n", i);
(rr) print i
$6 = 1509245372
(rr) reverse-continue
Continuing.

Breakpoint 1, main () at a.c:16
16          i = 0;

Karmaşık yazılımlarda hata ayıklarken, muhtemelen bir çökme noktasına kadar koşacak ve ardından derin bir çerçevenin içine düşeceksiniz. Bu durumda, bunu reverse-nextdaha yüksek karelerde için unutmayın, önce şunları yapmalısınız:

reverse-finish

o çerçeveye kadar, sadece her zamanki gibi yapmak upyeterli değil.

Bence rr'nin en ciddi sınırlamaları:

UndoDB, rr: https ://undo.io'ya ticari bir alternatiftir. Her ikisi de izleme / tekrar oynatma tabanlıdır, ancak özellikler ve performans açısından nasıl karşılaştırıldıklarından emin değilim.


Bunu ddd ile nasıl yapabileceğimi biliyor musun? Teşekkürler
spraff

@spraff Emin değilim, ama muhtemelen. Önce ddd'yi gdbserver'a bağlamayı deneyin. Bu işe yararsa, rr ile de çalışmalıdır.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
Ancak @spraff, ddd kullanmayın, gdb panosunu kullanın ;-) stackoverflow.com/questions/10115540/gdb-split-view-with-code/… Bu, normal GDB olduğu için kesinlikle çalışacaktır.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

3

Nathan Fellman şunu yazdı:

Ancak ters hata ayıklama yalnızca bir sonraki ve yazdığınız komutları geri almanıza izin veriyor mu, yoksa herhangi bir sayıda talimatı geri almanıza izin veriyor mu?

İstediğiniz sayıda talimatı geri alabilirsiniz. Örneğin, sadece ileriye giderken durduğunuz noktalarda durmakla sınırlı değilsiniz. Yeni bir kesme noktası ayarlayabilir ve geriye doğru koşabilirsiniz.

Örneğin, bir talimat için bir kesme noktası ayarlarsam ve o zamana kadar çalışmasına izin verirsem, bunu atlamış olsam bile önceki talimata geri dönebilir miyim?

Evet. Kesme noktasına koşmadan önce kayıt modunu açtığınız sürece.


2
Herhangi bir ters çözümün önemli bir parçası, onu bir noktada açmanız ve yalnızca o noktaya kadar tersine dönebilmenizdir. Bir makineyi tam tersi yönde çalıştırabilecek ve daha önce neler olduğuna dair bir tür kayıt olmadan neler olduğunu öğrenebilecek hiçbir sihir yoktur.
jakobengblom2

2

İşte ODB adlı başka ters ayıklayıcı şekilde işliyor. Ayıkla:

Omniscient Hata Ayıklama, bir programdaki her "ilgi noktası" nda (bir değer belirleme, bir yöntem çağrısı yapma, bir istisna atma / yakalama) "zaman damgaları" toplama ve ardından programcının bu zaman damgalarını kullanarak o programın geçmişi çalıştırılır.

ODB ... yüklenirken programın sınıflarına kod ekler ve program çalıştığında olaylar kaydedilir.

Sanırım gdb de aynı şekilde çalışıyor.


Bu, derleyiciye ve hata ayıklayıcıya bu ilginç noktaların nerede olduğunu söylemek için kodda yönergeler gerektirir mi?
Nathan Fellman

Hayır. Www.LambdaCS.com/debugger/debugger.html adresinde nasıl çalıştığını gösteren bir Java Web Start demosu var. Normal bir program gibi görünüyor. Zaten bu ODB, gdb hakkında bilmiyorum. Yine de çok havalı :)
demoncodemonkey

Gdb çözümünün hedef programı hiçbir şekilde DEĞİŞTİRMEZ. Hata ayıklamak için bir program düzenlemeniz gerekiyorsa, zamanlama farkı ve diğer rahatsızlıklar nedeniyle problemin ortadan kalkma ihtimali oldukça yüksektir. Tüm ticari revexec araçları, programın kendi kodunu değiştirmeyen bir tür harici kayıta dayanır.
jakobengblom2

@ jakobengblom2: Bence hedefi hafızasına yazarak değiştirmek, yürütmeyi taklit etmek veya sadece donanım kesme noktaları eklemek arasındaki farka çok fazla önem veriyorsunuz. Hepsi zamanlamayı değiştirir. Aslında, hedef enstrümantasyon muhtemelen zamanlamayı en az değiştirir.
Ben Voigt

2

Ters hata ayıklama, programı geriye doğru çalıştırabileceğiniz anlamına gelir; bu, bir sorunun nedenini bulmak için çok yararlıdır.

Her adım için tam makine durumunu kaydetmenize gerek yoktur, yalnızca değişiklikleri saklamanız gerekir. Muhtemelen hala oldukça pahalıdır.


Anlıyorum, ama yine de değişiklikleri kaydetmek için her değişiklikte yürütmeyi kesmeniz gerekiyor.
Nathan Fellman

Evet, bu doğru, ancak makineler şu anda oldukça hızlı ve insani açıdan yavaşlamanın tahammül edilemez olduğuna inanmıyorum. Valgrind ile karşılaştırılabilir, belki valgrind kadar yavaş değil.
Michael Snyder
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.