2010-07-09 19 views
6

Gdy debugowanie funkcję Zwykle używamZalecenia dotyczące "dynamicznego/interaktywnego" debugowania funkcji w R?

library(debug) 
mtrace(FunctionName) 
FunctionName(...) 

i że działa całkiem dobrze dla mnie.

Jednak czasami próbuję debugowania złożoną funkcję, która nie wiem. W takim przypadku mogę stwierdzić, że wewnątrz tej funkcji jest inna funkcja, którą chciałbym "przejść" ("debugowanie") - aby lepiej zrozumieć, jak działa cały proces.

Więc jeden sposób to zrobić byłoby zrobić:

library(debug) 
mtrace(FunctionName) 
FunctionName(...) 
# when finding a function I want to debug inside the function, run again: 
mtrace(FunctionName.SubFunction) 

Pytanie brzmi - jest lepszy/mądrzejszy sposób na interaktywne debugowanie (jak opisałem), że może brakować?

p.s: Wiem, że tam, gdzie różne pytania zadawane na ten temat na SO (patrz here). Nie mogłem jednak znaleźć podobnego pytania/rozwiązania do tego, o co tutaj prosiłem.

Odpowiedz

5

Nie do końca pewien co do przypadku użycia, ale jeśli wystąpi problem, można wywołać funkcję traceback(). To pokaże ścieżkę twojego wywołania funkcji przez stos aż do momentu, w którym pojawi się problem. Gdybyś był skłonny do pracy z góry, zadzwoń pod numer debug na każdą z funkcji podanych na liście, zanim wykonasz wywołanie funkcji. Następnie od samego początku przechodziłeś przez cały proces.

Oto przykład tego, jak można to zrobić w sposób bardziej systematyczny, poprzez stworzenie funkcji do kroku przez niego:

walk.through <- function() { 
    tb <- unlist(.Traceback) 
    if(is.null(tb)) stop("no traceback to use for debugging") 
    assign("debug.fun.list", matrix(unlist(strsplit(tb, "\\(")), nrow=2)[1,], envir=.GlobalEnv) 
    lapply(debug.fun.list, function(x) debug(get(x))) 
    print(paste("Now debugging functions:", paste(debug.fun.list, collapse=","))) 
} 

unwalk.through <- function() { 
    lapply(debug.fun.list, function(x) undebug(get(as.character(x)))) 
    print(paste("Now undebugging functions:", paste(debug.fun.list, collapse=","))) 
    rm(list="debug.fun.list", envir=.GlobalEnv) 
} 

Oto manekin przykład użycia go:

foo <- function(x) { print(1); bar(2) } 
bar <- function(x) { x + a.variable.which.does.not.exist } 
foo(2) 

# now step through the functions 
walk.through() 
foo(2) 

# undebug those functions again... 
unwalk.through() 
foo(2) 

IMO, to nie wydaje się być najrozsądniejszą rzeczą do zrobienia. Bardziej sensowne jest po prostu przejście do funkcji, w której pojawia się problem (tj. Na najniższym poziomie) i przejście do tyłu.

Już nakreślono logikę tego podstawowego rutyny w "favorite debugging trick".

+0

Dzięki Shane, Mogę użyć twojego kodu z mtrace, co może być miłe w niektórych przypadkach. Ale generalnie biorę twoją uwagę na temat debugowania bottom-up. –

+0

Cześć Shane, pomyślał. Czy możemy wyodrębnić listę funkcji z funkcji traceback, abyśmy mogli uruchomić twoją funkcję tylko na nich? –

+1

Dokładnie to robi moja funkcja przejścia. – Shane

5

Lubię options(error=recover) jak szczegółowe previously on SO. Wszystko następnie zatrzymuje się w punkcie błędu i można sprawdzić.

+0

Dziękuję Dirk. Moje pytanie wynika z przypadków gdzie błąd w moim kodzie pochodził z poprzednich kroków z dziwnymi przypadkami końca. Zgadzam się, że pierwszą rzeczą powinno być twoja opcja. A jeśli jesteśmy na ten temat, czy myślisz, że byłoby możliwe, aby poprosić o funkcję odzyskiwania za pomocą czegoś podobnego ? do mtrace (od {debug}) zamiast przeglądarce bazowej() –

+0

Czy próbowałeś 'opcje (error = mtrace)' – Shane

+0

teraz zrobiłem, to błędy z: [1] 1 błąd w barze (2): nie znaleziono obiektu "a.variable.which.does.not.exist" Erro r podczas opakowywania: niepoprawny pierwszy argument –

3

(jestem autorem pakietu „debug”, gdzie mieszka „mtrace”)

Jeżeli definicja „podfunkcji” mieszka poza „MyFunction”, następnie można po prostu mtrace „podfunkcji” i don” t potrzebujesz do mtrace "MyFunction". A funkcje działają szybciej, jeśli nie są "mtrace", więc dobrze jest śledzić tylko tyle, ile potrzebujesz. (Ale prawdopodobnie już wiesz o tym wszystkim!)

Jeśli "MyFunction" jest zdefiniowana tylko w "SubFunction", jedną lewą, która może pomóc, jest użycie warunkowego punktu przerwania w "MyFunction". Musisz "mtrace (MyFunction)", a następnie uruchomić go, a gdy pojawi się okno debugowania, dowiedz się, w którym wierszu zdefiniowano "MyFunction". Powiedzmy, że jest to linia 17.Następnie po powinno działać:

D (n)> BP (1, M) # nie przeszkadza pokazując okno do myfunction ponownie D (n)> BP (18 {mtrace (podrzędną), false}) D (n)> go()

Powinno być jasne, co to robi (lub będzie, jeśli spróbujesz).

Jedynymi wadami są: konieczność zrobienia tego ponownie po każdej zmianie kodu funkcji "MojaFunkcja"; spowolnienie, które może nastąpić, gdy samo "MyFunction" zostanie zmapowane.

Można także eksperymentować z dodawaniem argumentu "debug.sub" do "MyFunction", którego wartość domyślna to FALSE. W kodzie „MyFunction”, a następnie dodać ten wiersz bezpośrednio po definicji „podfunkcja”:

if (debug.sub) mtrace (podfunkcja)

że unika konieczności mtrace „MyFunction” się, ale wymaga od ciebie możliwości zmiany kodu.

+0

Witaj Mark, bardzo się cieszę, że przyłączyłeś się do dyskusji. Mocno popieram twój pakiet i cieszę się nim (prawie codziennie). Podane wskazówki są interesujące. Z powyższej dyskusji jest jedna zdolność, którą próbuję wymyślić, jak zrobić z mtrace - i to jest automatyczne "mtrace" -ing WSZYSTKICH (lub wybranych) funkcji, które są zaangażowane w błąd. Funkcja, którą Shane dał w swojej odpowiedzi, dobrze to demonstruje. Potrzeba pojawia się, gdy błąd pochodzi od punktu zerwania. Jeszcze raz dziękuję za cały kod i czas! –

Powiązane problemy