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