[runtime] Overwrite stacktrace for exception on re-throw. Fixes #1856.
[mono.git] / mono / profiler / mono-cov.c
1 /*
2  * mono-co.c: Coverage profiler
3  *
4
5  * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
6  */
7 #include <mono/metadata/profiler.h>
8 #include <mono/metadata/tokentype.h>
9 #include <mono/metadata/tabledefs.h>
10 #include <mono/metadata/debug-helpers.h>
11 #include <mono/metadata/assembly.h>
12 #include <string.h>
13 #include <glib.h>
14
15 /*
16  * Coverage profiler. Compile with:
17  * gcc -Wall -shared -o mono-profiler-cov.so mono-cov.c `pkg-config --cflags --libs mono`
18  * Install the binary where the dynamic loader can find it (/usr/local/lib, for example,
19  * or set the env var LD_LIBRARY_PATH to the directory where the file is).
20  * Then run mono with:
21  * mono --profile=cov:yourassembly test_suite.exe
22  * mono --profile=cov:yourassembly/namespace test_suite.exe
23  */
24
25 struct _MonoProfiler {
26         GHashTable *hash;
27         char* assembly_name;
28         char* class_name;
29         MonoAssembly *assembly;
30         GList *bb_coverage;
31 };
32
33 static void
34 get_assembly (MonoAssembly* ass, MonoProfiler *prof)
35 {
36         if (strcmp (prof->assembly_name, mono_image_get_name (mono_assembly_get_image (ass))) == 0)
37                 prof->assembly = ass;
38 }
39
40 static void
41 coverage_callback (MonoProfiler *prof, const MonoProfileCoverageEntry *entry)
42 {
43         char* cmsg;
44
45         if (entry->counter)
46                 return;
47
48         if (entry->filename) {
49                 cmsg = g_strdup_printf ("offset 0x%04x (%s: line: %d, col: %d)", 
50                         entry->iloffset, entry->filename, entry->line, entry->col);
51         } else {
52                 cmsg = g_strdup_printf ("offset 0x%04x", entry->iloffset);
53         }
54         prof->bb_coverage = g_list_append (prof->bb_coverage, cmsg);
55 }
56
57 static void
58 check_partial_coverage (MonoProfiler *prof, MonoMethod *method)
59 {
60         GList *tmp;
61         
62         mono_profiler_coverage_get (prof, method, coverage_callback);
63         if (prof->bb_coverage) {
64                 char *name = mono_method_full_name (method, TRUE);
65                 g_print ("Partial coverage: %s\n", name);
66                 g_free (name);
67                 for (tmp = prof->bb_coverage; tmp; tmp = tmp->next) {
68                         g_print ("\t%s\n", (char*)tmp->data);
69                         g_free (tmp->data);
70                 }
71                 g_list_free (prof->bb_coverage);
72                 prof->bb_coverage = NULL;
73         }
74 }
75
76 /* called at the end of the program */
77 static void
78 cov_shutdown (MonoProfiler *prof)
79 {
80         MonoImage *image;
81         MonoMethod *method;
82         int i;
83         char *name;
84
85         mono_assembly_foreach ((GFunc)get_assembly, prof);
86         if (!prof->assembly) {
87                 g_print ("Assembly '%s' was not loaded\n", prof->assembly_name);
88                 return;
89         }
90         image = mono_assembly_get_image (prof->assembly);
91         for (i = 1; i <= mono_image_get_table_rows (image, MONO_TABLE_METHOD); ++i) {
92                 MonoClass *klass;
93                 method = mono_get_method (image, i | MONO_TOKEN_METHOD_DEF, NULL);
94                 if (!method)
95                         continue;
96                 if ((mono_method_get_flags (method, NULL) & METHOD_ATTRIBUTE_ABSTRACT))
97                         continue;
98                 /* FIXME: handle icalls, runtime calls and synchronized methods */
99                 if (prof->class_name && *prof->class_name) {
100                         klass = mono_method_get_class (method);
101                         if (!strstr (mono_class_get_name (klass), prof->class_name) && !strstr (mono_class_get_namespace (klass), prof->class_name))
102                                 continue;
103                 }
104                 /*g_print ("check %s::%s, %p\n", method->klass->name, method->name, method);*/
105                 if (g_hash_table_lookup (prof->hash, method)) {
106                         /* the method was executed: check it was fully covered */
107                         check_partial_coverage (prof, method);
108                         continue;
109                 }
110                 name = mono_method_full_name (method, TRUE);
111                 g_print ("Not covered: %s\n", name);
112                 g_free (name);
113         }
114 }
115
116 static void
117 cov_method_enter (MonoProfiler *prof, MonoMethod *method)
118 {
119         /*g_print ("enter %s::%s, %p\n", method->klass->name, method->name, method);*/
120         g_hash_table_insert (prof->hash, method, GINT_TO_POINTER (1));
121 }
122
123 static void
124 cov_method_leave (MonoProfiler *prof, MonoMethod *method)
125 {
126 }
127
128 static gboolean
129 cov_coverage_filter(MonoProfiler *prof, MonoMethod *method)
130 {
131         return TRUE;
132 }
133
134 void
135 mono_profiler_startup (const char *desc);
136
137 /* the entry point */
138 void
139 mono_profiler_startup (const char *desc)
140 {
141         MonoProfiler *prof;
142
143         prof = g_new0 (MonoProfiler, 1);
144         prof->hash = g_hash_table_new (NULL, NULL);
145         if (strncmp ("cov:", desc, 4) == 0 && desc [4]) {
146                 char *cname;
147                 prof->assembly_name = g_strdup (desc + 4);
148                 cname = strchr (prof->assembly_name, '/');
149                 if (cname) {
150                         *cname = 0;
151                         prof->class_name = cname + 1;
152                 }
153         } else {
154                 prof->assembly_name = g_strdup ("mscorlib");
155         }
156
157         mono_profiler_install (prof, cov_shutdown);
158         
159         mono_profiler_install_enter_leave (cov_method_enter, cov_method_leave);
160         mono_profiler_install_coverage_filter (cov_coverage_filter);
161         mono_profiler_set_events (MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_COVERAGE);
162 }
163
164