2009-09-18 22 views
78

Jeśli używam długiego skryptu R z wiersza poleceń (R -slave script.R), w jaki sposób mogę go uzyskać, aby numery linii były błędne?Numery linii skryptu R w przypadku błędu?

Nie chcę, aby dodać polecenia debug do skryptu, jeśli to w ogóle możliwe - po prostu chcę R zachowywać się podobnie jak większość innych języków skryptowych ...

+20

Jakieś aktualizacje? Cztery cztery lata później, wydaje się, że problem nadal występuje, pomimo całego popularnego przyjęcia R. –

+0

Mam również bardzo długi skrypt R z dużą ilością małych wydruków, chcę wydrukować (podkreślenie) (podkreślenie) LINE/FILE (podkreślenie) (podkreślenie) (numery wierszy i nazwa skryptu), takie jak w C, zamiast kodów kreskowych w źródle. – mosh

+0

Nie wiem, czy R wewnętrznie naprawdę ma pojęcie "numerów linii". Jednak ma on pojęcie kompletnych zadań, tj. Zadań na najwyższym poziomie. Można na przykład łatwo zdefiniować procedurę obsługi zadania, aby określić, które zadanie najwyższego poziomu nie powiodło się. Oczywiście nie jest to wielka pociecha dla osób z dużymi sieciami lub dużymi oświadczeniami warunkowymi. – russellpierce

Odpowiedz

33

nie daje numer linii, ale powie to, gdzie awaria dzieje się na stosie wywołań, co jest bardzo pomocne:

traceback() 

[Edit:] po uruchomieniu skryptu z linii poleceń trzeba będzie pominąć jeden lub dwa połączenia, zobacz traceback() for interactive and non-interactive R sessions

nie jestem świadomy inny sposób to zrobić bez Podejrzani debugowania:

  1. debugowania()
  2. przeglądarka()
  3. opcje (błąd = odzyskać) [następnie opcji (błąd = NULL), aby go przywrócić]

You might want to look at this related post.

[Edit:] Niestety ... tylko zobaczył, że używasz tego z wiersza poleceń. W takim przypadku sugerowałbym pracę z funkcjami opcji (błędów). Oto prosty przykład:

options(error = quote({dump.frames(to.file=TRUE); q()})) 

Można utworzyć skomplikowany skrypt, jak chcesz na stan błędu, więc należy po prostu zdecydować, jakie informacje potrzebne do debugowania.

W przeciwnym razie, jeśli istnieją określone obszary, których dotyczy problem (na przykład połączenie z bazą danych), a następnie zawiń je w funkcji tryCatch().

10

Obsługa tego będzie dostępna w wersji 2.10 i nowszych. Duncan Murdoch właśnie pisał do r-devel wrz 10 2009 o findLineNum and setBreapoint:

I've just added a couple of functions to R-devel to help with 
debugging. findLineNum() finds which line of which function corresponds 
to a particular line of source code; setBreakpoint() takes the output of 
findLineNum, and calls trace() to set a breakpoint there. 

These rely on having source reference debug information in the code. 
This is the default for code read by source(), but not for packages. To 
get the source references in package code, set the environment variable 
R_KEEP_PKG_SOURCE=yes, or within R, set options(keep.source.pkgs=TRUE), 
then install the package from source code. Read ?findLineNum for 
details on how to 
tell it to search within packages, rather than limiting the search to 
the global environment. 

For example, 

x <- " f <- function(a, b) { 
      if (a > b) { 
       a 
      } else { 
       b 
      } 
     }" 


eval(parse(text=x)) # Normally you'd use source() to read a file... 

findLineNum("<text>#3") # <text> is a dummy filename used by parse(text=) 

This will print 

f step 2,3,2 in <environment: R_GlobalEnv> 

and you can use 

setBreakpoint("<text>#3") 

to set a breakpoint there. 

There are still some limitations (and probably bugs) in the code; I'll 
be fixing thos 
+0

Istnieją codziennie źródło r-devel (i pliki binarne dla Windoze) ... –

+0

Dzięki. Właśnie zapisałem się na listę mailingową r-devel. Unikałem r-pomocy, zakładając, że zablokuje ona moją skrzynkę odbiorczą (r-sig-finance już to robi). – Shane

+1

Naprawdę nie rozumiem, jak to działa z wiersza poleceń bez zaglądania w skrypcie R. –

10

Doing options(error=traceback) zapewnia trochę więcej informacji na temat zawartości linii prowadzących do błędu. Powoduje pojawienie się traceback, jeśli wystąpi błąd, a dla niektórych błędów ma numer linii poprzedzony prefiksem #. Ale jest to hit lub miss, wiele błędów nie otrzyma numerów linii.

+0

Nie działa dla mnie. Mam tylko jeden plik i nie pokazuje on numeru wiersza, po błędzie jest napisane "Brak dostępnego śledzenia". –

+0

@MarkLakata należy pamiętać, że jest to odpowiedź z 2012 roku, czyli 5 lat temu ... –

0

to zrobić poprzez ustawienie

options(show.error.locations = TRUE) 

Zastanawiam się tylko, dlaczego ta opcja nie jest domyślnie w R? Powinno tak być, jak w każdym innym języku.

+1

Aby uzyskać dodatkowe informacje na temat tej opcji, zobacz https://stat.ethz.ch/R-manual/R-devel/library/base /html/options.html –

+0

To działało, ale zostało wyłączone, ponieważ nie jest niezawodne. Myślę, że jest to próba zmuszenia Cię do korzystania z RStudio, które ostatecznie nie będzie wolne. –

+6

Wątpię w to. R core i RStudio to bardzo różne organizacje, a rdzeń R w szczególności jest zagorzałym open-sourcers. –

1

Określono globalną opcję R do obsługi błędów niekrytycznych dla mnie, wraz z niestandardowym przepływem pracy dla zachowania informacji o błędzie i sprawdzeniu tych informacji po awarii. Aktualnie korzystam z wersji R 3.4.1. Poniżej zamieściłem opis przepływu pracy, który działał dla mnie, a także kod, który ustawiłem opcję globalnej obsługi błędów w R.

Jak już skonfigurowałem, obsługa błędów tworzy również Plik RData zawierający wszystkie obiekty w pamięci roboczej w momencie wystąpienia błędu.Ten zrzut można odczytywać z powrotem do R za pomocą load(), a następnie różne środowiska, które istniały w momencie wystąpienia błędu, można kontrolować interaktywnie za pomocą debugger(errorDump).

będę pamiętać, że udało mi się uzyskać numery linii w traceback() wyjściu z jakichkolwiek funkcji niestandardowych w stosie, ale tylko wtedy, gdy użyłem opcji keep.source=TRUE Dzwoniąc source() dla wszelkich niestandardowych funkcji wykorzystywanych w moim skrypcie. Bez tej opcji ustawienie opcji globalnej obsługi błędów, jak poniżej, wysłało pełne wyjście traceback() do dziennika błędów o nazwie error.log, ale numery linii nie były dostępne.

Oto ogólne kroki, które podjąłem w moim obiegu pracy i jak mogłem uzyskać dostęp do zrzutu pamięci i dziennika błędów po nieinteraktywnym niepowodzeniu R.

  1. W górnej części skryptu głównego, który wywoływałam z wiersza poleceń, umieściłem następujący kod: Ustawia globalną opcję obsługi błędów dla sesji R. Mój główny skrypt nazywa się myMainScript.R. Poszczególne wiersze w kodzie mają komentarze po tym, jak opisują, co robią. Zasadniczo, przy tej opcji, gdy R napotka błąd, który wyzwala stop(), utworzy plik zrzutu RData (* .rda) pamięci roboczej we wszystkich aktywnych środowiskach w katalogu ~/myUsername/directoryForDump i zapisze również dziennik błędów o nazwie error.log z pewnymi przydatnymi informacje do tego samego katalogu. Możesz zmodyfikować ten fragment, aby dodać inne sposoby obsługi po błędzie (np. Dodać sygnaturę czasową do plików zrzutu i nazw plików dziennika błędów itp.).

    options(error = quote({ 
        setwd('~/myUsername/directoryForDump'); # Set working directory where you want the dump to go, since dump.frames() doesn't seem to accept absolute file paths. 
        dump.frames("errorDump", to.file=TRUE, include.GlobalEnv=TRUE); # First dump to file; this dump is not accessible by the R session. 
        sink(file="error.log"); # Specify sink file to redirect all output. 
        dump.frames(); # Dump again to be able to retrieve error message and write to error log; this dump is accessible by the R session since not dumped to file. 
        cat(attr(last.dump,"error.message")); # Print error message to file, along with simplified stack trace. 
        cat('\nTraceback:'); 
        cat('\n'); 
        traceback(2); # Print full traceback of function calls with all parameters. The 2 passed to traceback omits the outermost two function calls. 
        sink(); 
        q()})) 
    
  2. Upewnij się, że z głównego scenariusza i wszystkich kolejnych wywołań funkcji, w dowolnym momencie funkcja jest pozyskiwane, opcja keep.source=TRUE służy. Oznacza to, że do źródła funkcji użyłbyś source('~/path/to/myFunction.R', keep.source=TRUE). Jest to wymagane, aby dane wyjściowe traceback() zawierały numery linii. Wygląda na to, że możesz również ustawić tę opcję globalnie, używając options(keep.source=TRUE), ale nie testowałem tego, aby sprawdzić, czy działa. Jeśli nie potrzebujesz numerów linii, możesz pominąć tę opcję.

  3. Z terminalu (poza R) wywołaj główny skrypt w trybie wsadowym, używając Rscript myMainScript.R. Spowoduje to uruchomienie nowej nieinteraktywnej sesji R i uruchomi skrypt myMainScript.R. Fragment kodu podany w kroku 1, który został umieszczony na górze myMainScript.R, ustawia opcję obsługi błędów dla nieinteraktywnej sesji R.
  4. Napotkać błąd w trakcie wykonywania myMainScript.R. Może to być główny skrypt lub zagnieżdżone kilka funkcji. Po napotkaniu błędu obsługa zostanie wykonana zgodnie z opisem w kroku 1, a sesja R zostanie zakończona.
  5. Plik zrzutu RData o nazwie errorDump.rda i dziennik błędów o nazwie error.log są tworzone w katalogu określonym przez '~/myUsername/directoryForDump' w ustawieniu globalnej opcji obsługi błędów.
  6. W czasie wolnym przejrzyj error.log, aby przejrzeć informacje o błędzie, w tym sam komunikat o błędzie i pełny ślad stosu prowadzący do błędu. Oto przykład dziennika wygenerowanego w wyniku błędu; Uwaga numery po znaku # są numery linii błędu w różnych punktach w stos wywołań:

    Error in callNonExistFunc() : could not find function "callNonExistFunc" 
    Calls: test_multi_commodity_flow_cmd -> getExtendedConfigDF -> extendConfigDF 
    
    Traceback: 
    3: extendConfigDF(info_df, data_dir = user_dir, dlevel = dlevel) at test_multi_commodity_flow.R#304 
    2: getExtendedConfigDF(config_file_path, out_dir, dlevel) at test_multi_commodity_flow.R#352 
    1: test_multi_commodity_flow_cmd(config_file_path = config_file_path, 
    spot_file_path = spot_file_path, forward_file_path = forward_file_path, 
    data_dir = "../", user_dir = "Output", sim_type = "spot", 
    sim_scheme = "shape", sim_gran = "hourly", sim_adjust = "raw", 
    nsim = 5, start_date = "2017-07-01", end_date = "2017-12-31", 
    compute_averages = opt$compute_averages, compute_shapes = opt$compute_shapes, 
    overwrite = opt$overwrite, nmonths = opt$nmonths, forward_regime = opt$fregime, 
    ltfv_ratio = opt$ltfv_ratio, method = opt$method, dlevel = 0) 
    
  7. w swoim czasie wolnym, można załadować errorDump.rda w sesji interaktywnej R używając load('~/path/to/errorDump.rda'). Po załadowaniu, zadzwoń pod numer debugger(errorDump), aby przeglądać wszystkie obiekty R w pamięci w dowolnym z aktywnych środowisk. Aby uzyskać więcej informacji, zobacz pomoc R na stronie debugger().

Ten przepływ pracy jest niezwykle pomocne podczas uruchamiania R w pewnego rodzaju środowisku produkcyjnym, gdzie trzeba non-interaktywne sesje R są inicjowane w linii poleceń i chcesz zachowane informacje o nieoczekiwanych błędów. Możliwość zrzucenia pamięci do pliku, którego można użyć do sprawdzenia pamięci roboczej w momencie wystąpienia błędu, wraz z numerem linii błędu w stosie wywołań, ułatwia szybkie, pośmiertne debugowanie przyczyny błędu.

Powiązane problemy