Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fri Nov 24 16:25:51 CET 2006 Paolo Molaro <lupus@ximian.com>

	* CoverageModel.cs: updated to use Cecil to load assemblies
	so the tool can be used with both 1.0 and 2.0 runtimes.
	Made it handle the conversion between the different name
	conventions for nested types.
	* coverage.c: include the assembly filename in the output so
	we don't have to try and emulate the runtime assembly load
	process and we can find the used assembly right away.


svn path=/trunk/monocov/; revision=68432
  • Loading branch information...
commit 700a3591bb5982481b1949b86261663fd914bb2d 1 parent cbf89b1
@illupus illupus authored
View
16 ChangeLog
@@ -1,3 +1,19 @@
+
+Fri Nov 24 16:25:51 CET 2006 Paolo Molaro <lupus@ximian.com>
+
+ * CoverageModel.cs: updated to use Cecil to load assemblies
+ so the tool can be used with both 1.0 and 2.0 runtimes.
+ Made it handle the conversion between the different name
+ conventions for nested types.
+ * coverage.c: include the assembly filename in the output so
+ we don't have to try and emulate the runtime assembly load
+ process and we can find the used assembly right away.
+
+Fri Nov 24 16:23:38 CET 2006 Paolo Molaro <lupus@ximian.com>
+
+ * gui/gtk/*.cs: usability improvements and changes to make
+ it run with more recent versions of glade-sharp.
+
2006-04-04 Jacob Ilsø Christensen <jacobilsoe@gmail.com>
* CoverageModel.cs: Removed redundant for loops
View
5 ClassCoverageItem.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections;
+using Mono.Cecil;
namespace MonoCov {
@@ -15,7 +16,11 @@ public class ClassCoverageItem : CoverageItem {
public SourceFileCoverageData sourceFile;
// The type object representing this class
+#if USE_REFLECTION
public Type type;
+#else
+ public TypeDefinition type;
+#endif
// Contains MethodBase -> MethodCoverageData mappings
public Hashtable methodsByMethod;
View
132 CoverageModel.cs
@@ -2,9 +2,12 @@
using System.Collections;
using System.Xml;
using System.IO;
+using System.Text;
using System.Reflection;
using System.Text.RegularExpressions;
using Mono.CompilerServices.SymbolWriter;
+using Mono.Cecil;
+using Mono.Cecil.Metadata;
namespace MonoCov
{
@@ -83,7 +86,10 @@ private void LoadAssemblies (XmlDocument dom)
foreach (XmlNode n in dom.GetElementsByTagName ("assembly")) {
string assemblyName = n.Attributes ["name"].Value;
string guid = n.Attributes ["guid"].Value;
+ string filename = n.Attributes ["filename"].Value;
+ MonoSymbolFile symbolFile;
+#if USE_REFLECTION
Assembly assembly = Assembly.Load (assemblyName);
MethodInfo getguid = typeof (Module).GetMethod (
@@ -96,10 +102,10 @@ private void LoadAssemblies (XmlDocument dom)
if (assembly_guid != new Guid (guid)) {
Console.WriteLine ("WARNING: Loaded version of assembly " + assembly + " is different from the version used to collect coverage data.");
}
+ } else {
+ Console.WriteLine ("WARNING: Can't verify the guid of " + assembly);
}
- MonoSymbolFile symbolFile;
-
loadedAssemblies [assemblyName] = assembly;
Console.Write ("Reading symbols for " + assembly + " ...");
@@ -110,6 +116,23 @@ private void LoadAssemblies (XmlDocument dom)
symbolFiles [assembly] = symbolFile;
Console.WriteLine (" (" + symbolFile.SourceCount + " files, " + symbolFile.MethodCount + " methods)");
}
+#else
+ AssemblyDefinition assembly = AssemblyFactory.GetAssembly (filename);
+ ModuleDefinition module = assembly.MainModule;
+ if (module.Mvid != new Guid (guid)) {
+ Console.WriteLine ("WARNING: Loaded version of assembly " + assembly + " is different from the version used to collect coverage data.");
+ }
+ loadedAssemblies [assemblyName] = assembly;
+
+ Console.Write ("Reading symbols for " + assemblyName + " ...");
+ symbolFile = MonoSymbolFile.ReadSymbolFile (filename + ".mdb");
+ if (symbolFile == null)
+ Console.WriteLine (" (No symbols found)");
+ else {
+ symbolFiles [assembly] = symbolFile;
+ Console.WriteLine (" (" + symbolFile.SourceCount + " files, " + symbolFile.MethodCount + " methods)");
+ }
+#endif
}
}
@@ -120,6 +143,46 @@ private void LoadFilters (XmlDocument dom)
}
}
+#if USE_REFLECTION
+ static Type LoadType (Assembly assembly, string name) {
+ Type type = assembly.GetType (name);
+ if (type != null)
+ return type;
+ int last_dot = name.LastIndexOf ('.');
+ // covert names from IL to reflection naming
+ // needed to deal with nested types
+ while (last_dot >= 0) {
+ StringBuilder sb = new StringBuilder (name);
+ sb [last_dot] = '/';
+ name = sb.ToString ();
+ type = assembly.GetType (name);
+ if (type != null)
+ return type;
+ last_dot = name.LastIndexOf ('.');
+ }
+ return null;
+ }
+#else
+ static TypeDefinition LoadType (AssemblyDefinition assembly, string name) {
+ TypeDefinition type = assembly.MainModule.Types [name];
+ if (type != null)
+ return type;
+ int last_dot = name.LastIndexOf ('.');
+ // covert names from IL to reflection naming
+ // needed to deal with nested types
+ while (last_dot >= 0) {
+ StringBuilder sb = new StringBuilder (name);
+ sb [last_dot] = '/';
+ name = sb.ToString ();
+ type = assembly.MainModule.Types [name];
+ if (type != null)
+ return type;
+ last_dot = name.LastIndexOf ('.');
+ }
+ return null;
+ }
+#endif
+
public void ReadFromFile (string fileName)
{
namespaces = new Hashtable ();
@@ -155,14 +218,16 @@ public void ReadFromFile (string fileName)
string methodName = n.Attributes ["name"].Value;
string token = n.Attributes ["token"].Value;
string cov_info = n.FirstChild.Value;
+ int itok = int.Parse (token);
+#if USE_REFLECTION
Assembly assembly = (Assembly)loadedAssemblies [assemblyName];
MonoSymbolFile symbolFile = (MonoSymbolFile)symbolFiles [assembly];
if (symbolFile == null)
continue;
- Type t = assembly.GetType (className);
+ Type t = LoadType (assembly, className);
if (t == null) {
Console.WriteLine ("ERROR: Unable to resolve type " + className + " in " + assembly);
continue;
@@ -182,6 +247,29 @@ public void ReadFromFile (string fileName)
MethodBase monoMethod = module.ResolveMethod(Int32.Parse(token));
ProcessMethod (monoMethod, entry, klass, methodName, cov_info);
+#else
+ AssemblyDefinition assembly = (AssemblyDefinition)loadedAssemblies [assemblyName];
+ MonoSymbolFile symbolFile = (MonoSymbolFile)symbolFiles [assembly];
+
+ if (symbolFile == null)
+ continue;
+
+ TypeDefinition t = LoadType (assembly, className);
+ if (t == null) {
+ Console.WriteLine ("ERROR: Unable to resolve type " + className + " in " + assembly);
+ continue;
+ }
+
+ ClassCoverageItem klass = ProcessClass (t);
+
+ MethodEntry entry = symbolFile.GetMethodByToken (itok);
+
+ MethodDefinition monoMethod = assembly.MainModule.LookupByToken (
+ new MetadataToken ((TokenType)(itok & 0xff000000), (uint)(itok & 0xffffff)))
+ as MethodDefinition;
+ //Console.WriteLine (monoMethod);
+ ProcessMethod (monoMethod, entry, klass, methodName, cov_info);
+#endif
}
msec2 = DateTime.Now.Ticks / 10000;
@@ -190,6 +278,7 @@ public void ReadFromFile (string fileName)
// Add info for klasses for which we have no coverage
+#if USE_REFLECTION
foreach (Assembly assembly in loadedAssemblies.Values) {
foreach (Type t in assembly.GetTypes ()) {
ProcessClass (t);
@@ -210,6 +299,27 @@ public void ReadFromFile (string fileName)
}
}
}
+#else
+ foreach (AssemblyDefinition assembly in loadedAssemblies.Values) {
+ foreach (TypeDefinition t in assembly.MainModule.Types) {
+ ProcessClass (t);
+ }
+ }
+
+ // Add info for methods for which we have no coverage
+ foreach (ClassCoverageItem klass in classes.Values) {
+ foreach (MethodDefinition mb in klass.type.Methods) {
+ MonoSymbolFile symbolFile = (MonoSymbolFile)symbolFiles [klass.type.Module.Assembly];
+ if (symbolFile == null)
+ continue;
+
+ if (! klass.methodsByMethod.ContainsKey (mb)) {
+ MethodEntry entry = symbolFile.GetMethodByToken ((int)mb.MetadataToken.ToUInt());
+ ProcessMethod (mb, entry, klass, mb.Name, null);
+ }
+ }
+ }
+#endif
msec2 = DateTime.Now.Ticks / 10000;
Console.WriteLine ("Additional classes: " + (msec2 - msec) + " msec");
@@ -356,7 +466,11 @@ private void computeMethodCoverage (MethodCoverageItem method, LineNumberEntry[]
}
}
+#if USE_REFLECTION
private ClassCoverageItem ProcessClass (Type t)
+#else
+ private ClassCoverageItem ProcessClass (TypeDefinition t)
+#endif
{
string className = t.FullName;
int nsindex = className.LastIndexOf (".");
@@ -403,14 +517,22 @@ private ClassCoverageItem ProcessClass (Type t)
klass.type = t;
klass.parent = ns;
+#if USE_REFLECTION
klass.filtered = IsFiltered ("[" + t.Assembly + "]" + className);
+#else
+ klass.filtered = IsFiltered ("[" + t.Module.Name + "]" + className);
+#endif
classes [className] = klass;
}
return klass;
}
+#if USE_REFLECTION
private void ProcessMethod (MethodBase monoMethod, MethodEntry entry, ClassCoverageItem klass, string methodName, string cov_info)
+#else
+ private void ProcessMethod (MethodDefinition monoMethod, MethodEntry entry, ClassCoverageItem klass, string methodName, string cov_info)
+#endif
{
if (entry == null)
// Compiler generated, abstract method etc.
@@ -429,7 +551,11 @@ MethodCoverageItem method
method.startLine = start_line;
method.endLine = end_line;
+#if USE_REFLECTION
method.filtered = IsFiltered ("[" + monoMethod.DeclaringType.Assembly + "]" + monoMethod.DeclaringType + "::" + monoMethod.Name);
+#else
+ method.filtered = IsFiltered ("[" + monoMethod.DeclaringType.Module.Name + "]" + monoMethod.DeclaringType + "::" + monoMethod.Name);
+#endif
klass.methodsByMethod [monoMethod] = method;
View
3  Makefile
@@ -2,6 +2,7 @@
PROJECTNAME = monocov
GUI = gtk
MONO_ROOT = ../mono
+LIBS=-r:Mono.Cecil
all: monocov.exe libmono-profiler-monocov.so symbols.exe
@@ -33,7 +34,7 @@ SRCS = \
$(GUI_SRCS)
monocov.exe: $(SRCS) style.xsl .gui-$(GUI)
- gmcs -debug /target:exe /out:$@ -define:GUI_$(GUI) -r:Mono.CompilerServices.SymbolWriter -r:Mono.GetOptions $(GUI_LIBS) $(SRCS) -resource:style.xsl,style.xsl -resource:trans.gif,trans.gif
+ gmcs -debug /target:exe /out:$@ -define:GUI_$(GUI) $(LIBS) -r:Mono.CompilerServices.SymbolWriter -r:Mono.GetOptions $(GUI_LIBS) $(SRCS) -resource:style.xsl,style.xsl -resource:trans.gif,trans.gif
.gui-gtk:
@rm -f .gui-*
View
43 README
@@ -2,19 +2,16 @@
1. INTRODUCTION
---------------
- MonoCov is a line coverage analysis program for mono. It can be used to
+MonoCov is a line coverage analysis program for mono. It can be used to
display coverage data collected while running a .NET program. There are two
-types of GUI interfaces, one implemented using Qt#, while the other is
-implemented using Gtk#. The Qt# version is more advanced.
+parts in the tool: a profiler module which is used during the execution of
+the program you want to gather coverage data from and a Gtk# user interface.
1.5 REQUIREMENTS
----------------
-The runtime parts are tested with the then current Mono CVS. The Qt# GUI
-requires a patched version of the Qt# library which is included in the
-distribution. This file is named Qt-monocov.dll, and the original Qt.dll should
-be replace with this file. The upcoming Qt# 0.7.1 release fixes most of the
-problems, so this won't be neccessary in the future.
+Recent Mono and Gtk# releases. You also need Mono.Cecil installed
+or copied in the source dir and where the programs are run from..
2. USAGE
--------
@@ -25,12 +22,12 @@ problems, so this won't be neccessary in the future.
To produce coverage info for an .NET program, compile it with the -debug
switch to generate debug information. After this, run the program as follows:
-$ ./mono --profile=monocov prog.exe
+$ mono --debug --profile=monocov prog.exe
This will produce a coverage data file called prog.exe.cov. You can run the
analyser program as follows:
-$ ./mono monocov.exe prog.exe.cov
+$ mono monocov.exe prog.exe.cov
This will display the class hierarchy of the program with the corresponding
coverage information.
@@ -40,7 +37,7 @@ generated. Filters are string which are applied agains the fully qualified
names of classes, e.g. [assemblyname]classname. You can specify filters
directly on the command line:
-$ ./mono --profile=monocov:-Security,-[System.Xml] prog.exe
+$ mono --debug --profile=monocov:-Security,-[System.Xml] prog.exe
There are two types of filters: include filters, whose name begins with '+',
and exclude filters, whose name begins with '-'. Include filters are checked
@@ -48,7 +45,7 @@ before exclude filters.
For example:
-$ ./mono --profile=monocov:+[corlib],-Hashtable prog.exe
+$ mono --debug --profile=monocov:+[mscorlib],-Hashtable prog.exe
This will collect coverage info for all classes in corlib, except the ones
whose name contains 'Hashtable'.
@@ -56,13 +53,13 @@ whose name contains 'Hashtable'.
2.2 ANALYSIS
------------
- The collected coverage data can be browsed using the monocov.exe program.
+ The collected coverage data can be browsed using the monocov program.
This program will read the data file produced by the profiler module, and
display its contents in a hierarchical fashion.
It is also possible to export the contents of a data file into XML, which
can be viewed in an XSL capable browser like mozilla.
To export the data as XML, run monocov like this:
- monocov.exe --export-xml=<DEST DIR> <DATA FILE NAME>
+ monocov --export-xml=<DEST DIR> <DATA FILE NAME>
The generated XML files use a default stylesheet which is a bit ugly. It would
be good if somebody could contribute a better one :)
@@ -70,8 +67,8 @@ be good if somebody could contribute a better one :)
2.5 KNOWN BUGS
--------------
-Due to some memory management problems in Qt#, the GUI will most likely crash
-on exit. It will also crash if the user tries to load a second data file.
+In the source code view (when double-clicking on an entry), the view is not
+scrolled to the first line of the method.
3. UTILITY PROGRAMS
-------------------
@@ -112,19 +109,6 @@ the mono devel list (mono-devel-list@ximian.com)
RANDOM NOTES:
-------------
-- There is a bug in QString:~QString (): it should be
- if (this != Null)
- qt_del_QString (rawObject);
-
-- Mono finalizes QApplication before QMainWindow, leading to
- invalid memory read errors.
-
-- Mono frees the the string[] array passed to qt_new_QMainWindow, leading to
- invalid read errors.
-
-- DecimalTest2:.ctor () causes the register allocator to allocate more than
- 2^16 registers, overflowing MonoInst->dreg.
-
- Methods with strange debugging info:
- System.Collections.Hashtable..cctor()
- SortedList+SynchedSortedList::this [key]
@@ -144,7 +128,6 @@ TODO:
- Implement merging of coverage results
- put namespaces above classes in the hierarchy
- handle missing source files
-- QDesigner
- Scintilla
- html output (with XSLT)
- use Xml serialization in SyntaxHighlighter
View
7 coverage.c
@@ -242,7 +242,7 @@ output_filters (MonoProfiler *prof, FILE *outfile)
static void
output_assembly (MonoAssembly *assembly, MonoAssembly *assembly2, FILE *outfile)
{
- fprintf (outfile, "\t<assembly name=\"%s\" guid=\"%s\"/>\n", assembly->image->assembly_name, assembly->image->guid);
+ fprintf (outfile, "\t<assembly name=\"%s\" guid=\"%s\" filename=\"%s\"/>\n", assembly->image->assembly_name, assembly->image->guid, mono_image_get_filename (assembly->image));
}
static int count;
@@ -313,7 +313,10 @@ coverage_shutdown (MonoProfiler *prof)
fprintf (outfile, "<?xml version=\"1.0\"?>\n");
fprintf (outfile, "<coverage>\n");
- output_filters (prof, outfile);
+ /*
+ * The UI doesn't deal well with this enabled.
+ * output_filters (prof, outfile);
+ */
g_hash_table_foreach (prof->assemblies, (GHFunc)output_assembly, outfile);
Please sign in to comment.
Something went wrong with that request. Please try again.