2011-09-29 3 views
3

Je dispose d'un fichier de test très court:pile d'impression traces

let print_backtrace() = try raise Not_found with 
    Not_found -> Printexc.print_backtrace stdout;; 

let f() = print_backtrace(); Printf.printf "this is to make f non-tail-recursive\n";; 

f(); 

Je compiler et exécuter:

% ocamlc -g test.ml  
% OCAMLRUNPARAM=b ./a.out 
Raised at file "test.ml", line 1, characters 35-44 
this is to make f non-tail-recursive 

Pourquoi ne f répertorié dans la trace de la pile? Comment puis-je écrire une fonction qui va imprimer une trace de pile de l'endroit à partir duquel elle est appelée?

Répondre

5

Voici le code pour faire ce que j'ai suggéré. Je recommande d'utiliser ocamldebug si cela est possible, ce code est beaucoup trop compliqué. Mais cela fonctionne sur mon système pour cet exemple simple.

let print_backtrace() = 
    match Unix.fork() with 
    | 0 -> raise Not_found 
    | pid -> let _ = Unix.waitpid [] pid in() 

let f() = 
    begin 
    print_backtrace(); 
    Printf.printf "after the backtrace\n"; 
    end 

;; 

f() 

Voici un test.

$ /usr/local/ocaml312/bin/ocamlc unix.cma -g test3.ml 
$ OCAMLRUNPARAM=b a.out 
Fatal error: exception Not_found 
Raised at file "test3.ml", line 3, characters 17-26 
Called from file "test3.ml", line 8, characters 4-22 
Called from file "test3.ml", line 14, characters 0-4 
after the backtrace 

j'ai réalisé qu'en raison de l'exception uncaught, vous n'avez vraiment aucun contrôle sur la façon dont les sorties de processus enfant. C'est l'une des raisons pour lesquelles ce code est beaucoup trop compliqué. S'il vous plaît ne me blâmez pas si cela ne fonctionne pas pour vous, mais j'espère que cela s'avérera utile.

J'ai testé le code sur Mac OS X 10.6.8 en utilisant OCaml 3.12.0. Cordialement,

6

La documentation Printexc.print_backtrace dit:

La liste des backtrace emplacements de programme où a été élevé et où il a été propagé l'exception plus récemment soulevé par les appels de fonction.

Il semble effectivement faire la bonne chose. L'exception n'a pas été propagée via f.

Si je déplace l'appel à Printexc.print_backtrace en dehors de l'appel à f, je vois un backtrace complet.

$ cat test2.ml 
let print_backtrace() = raise Not_found 

let f() = let res = print_backtrace() in res ;; 

try f() with Not_found -> Printexc.print_backtrace stdout 
$ /usr/local/ocaml312/bin/ocamlc -g test2.ml 
$ OCAMLRUNPARAM=b a.out 
Raised at file "test2.ml", line 1, characters 31-40 
Called from file "test2.ml", line 3, characters 21-39 
Called from file "test2.ml", line 5, characters 4-8 
+0

Cela répond à la moitié de ma question, alors merci pour cela. Est-ce que je devrais prendre le silence sur l'autre moitié pour indiquer qu'il n'est pas possible de faire ce que je veux, c'est-à-dire d'imprimer une trace de pile complète et continuer? –

+3

Je ne vois pas un bon moyen de le faire sans piratage sur le runtime OCaml. Peut-être courir sous ocamldebug? Ou puisque vous semblez fonctionner sous Unix, si ce n'est que pour une exécution rapide de dépannage, vous pouvez fork() votre processus et déclencher une exception non capturée dans le processus enfant (qui imprimera une trace de pile complète), puis quitter dans l'enfant et continuer dans le processus parent. J'ai fait quelque chose comme ça en C il y a des années et ça a marché pour moi. Mais c'est moche et susceptible de gâcher quelque chose. Si vous essayez ceci (probablement une mauvaise idée), appelez _Exit() dans l'enfant pour éviter de vider les tampons. Regards, –

+0

Si compilé au code natif, vous pouvez essayer d'utiliser des fonctions standard qui impriment la pile, par ex. libunwind ou backtrace de glibc. Notez que les résultats peuvent ne pas être très jolis mais généralement suffisants pour identifier le problème. – ygrek

Questions connexes