Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Initial revision

svn path=/trunk/monocov/; revision=15853
  • Loading branch information...
commit cb203a9979f943df538a31ff27d6393dec873547 0 parents
Zoltan Varga authored July 02, 2003
33  ChangeLog
... ...
@@ -0,0 +1,33 @@
  1
+2003-07-02  Zoltan Varga  <vargaz@freemail.hu>
  2
+
  3
+	* all: Initial import into CVS.
  4
+
  5
+2003-07-01  Zoltan Varga  <vargaz@freemail.hu>
  6
+
  7
+	* HtmlExporter.cs: Implemented HTML exporter.
  8
+
  9
+	* style.xsl: Small modifications needed by the HTML export method.
  10
+
  11
+2003-06-07  Zoltan Varga  <vargaz@freemail.hu>
  12
+
  13
+	* New version with many enchantments.
  14
+
  15
+2003-01-06  Zoltan Varga  <vargaz@freemail.hu>
  16
+
  17
+	* gui/qt/SourceView.cs: fixed viewing of source files which contain
  18
+	tags recognized by QTextEdit.
  19
+
  20
+2003-01-05  Zoltan Varga  <vargaz@freemail.hu>
  21
+
  22
+	* coverage.c: Handle nested types correctly.
  23
+
  24
+2002-12-30  Zoltan Varga  <vargaz@freemail.hu>
  25
+
  26
+	* coverage.c README: Added filters to the back end. They can be used by
  27
+	passing the '--coverage-args filterfile=<NAME>' argument on the command
  28
+	line.
  29
+
  30
+2002-12-28  Zoltan Varga  <vargaz@freemail.hu>
  31
+
  32
+	* first public version.
  33
+
54  ClassCoverageItem.cs
... ...
@@ -0,0 +1,54 @@
  1
+
  2
+using System;
  3
+using System.Collections;
  4
+
  5
+namespace MonoCov {
  6
+
  7
+public class ClassCoverageItem : CoverageItem {
  8
+
  9
+	/// the namespace to which this class belongs
  10
+	public string name_space;
  11
+
  12
+	/// the scoped name of this class
  13
+	public string name;
  14
+
  15
+	public SourceFileCoverageData sourceFile;
  16
+
  17
+	// The type object representing this class
  18
+	public Type type;
  19
+
  20
+	// Contains MethodBase -> MethodCoverageData mappings
  21
+	public Hashtable methodsByMethod;
  22
+
  23
+	public ClassCoverageItem (CoverageItem parent) : base (parent) {
  24
+		methodsByMethod = new Hashtable ();
  25
+	}
  26
+
  27
+	public ArrayList Methods {
  28
+		get {
  29
+			if (children == null)
  30
+				return new ArrayList (0);
  31
+			else
  32
+				return children;
  33
+		}
  34
+	}
  35
+
  36
+	public string FullName {
  37
+		get {
  38
+			if ((name_space == "") || (name_space == "<GLOBAL>"))
  39
+				return name;
  40
+			else
  41
+				return name_space + "." + name;
  42
+		}
  43
+	}
  44
+
  45
+	public string Namespace {
  46
+		get {
  47
+			if (name_space == "<GLOBAL>")
  48
+				return "";
  49
+			else
  50
+				return name_space;
  51
+		}
  52
+	}
  53
+}
  54
+}
111  CoverageItem.cs
... ...
@@ -0,0 +1,111 @@
  1
+
  2
+using System;
  3
+using System.Collections;
  4
+
  5
+namespace MonoCov {
  6
+
  7
+public abstract class CoverageItem {
  8
+
  9
+	public int hit;
  10
+	public int missed;
  11
+	public double coveragePercent;
  12
+
  13
+	public bool filtered;
  14
+
  15
+	public CoverageItem parent;
  16
+
  17
+	public ArrayList children;
  18
+
  19
+	public CoverageItem () {
  20
+		hit = 0;
  21
+		missed = 0;
  22
+		coveragePercent = 0.0;
  23
+	}
  24
+
  25
+	public CoverageItem (CoverageItem parent) : this () {
  26
+		if (parent != null)
  27
+			parent.AddChildren (this);
  28
+	}
  29
+
  30
+	public void AddChildren (CoverageItem item) {
  31
+		if (children == null)
  32
+			children = new ArrayList ();
  33
+		children.Add (item);
  34
+		item.parent = this;
  35
+	}
  36
+
  37
+	public virtual bool IsLeaf {
  38
+		get {
  39
+			return false;
  40
+		}
  41
+	}
  42
+
  43
+	public int ChildCount {
  44
+		get {
  45
+			if (children == null)
  46
+				return 0;
  47
+			else
  48
+				return children.Count;
  49
+		}
  50
+	}
  51
+
  52
+	public void setCoverage (int hit, int missed) {
  53
+		this.hit = hit;
  54
+		this.missed = missed;
  55
+		if (hit + missed == 0)
  56
+			coveragePercent = 100.0;
  57
+		else
  58
+			coveragePercent = (double)hit / (hit + missed);
  59
+	}
  60
+
  61
+	public void computeCoveragePercent () {
  62
+		if (hit + missed == 0)
  63
+			coveragePercent = 100.0;
  64
+		else
  65
+			coveragePercent = (double)hit / (hit + missed);
  66
+	}
  67
+
  68
+	public void computeCoverage () {
  69
+		computeCoverage (false);
  70
+	}
  71
+
  72
+	public void computeCoverage (bool recurse) {
  73
+		if (IsLeaf)
  74
+			return;
  75
+
  76
+		hit = 0;
  77
+		missed = 0;
  78
+
  79
+		if (children != null) {
  80
+			foreach (CoverageItem item in children) {
  81
+				if (!item.filtered) {
  82
+					if (recurse)
  83
+						item.computeCoverage (recurse);
  84
+					hit += item.hit;
  85
+					missed += item.missed;
  86
+				}
  87
+			}
  88
+		}
  89
+
  90
+		computeCoveragePercent ();
  91
+	}
  92
+
  93
+	public void recomputeCoverage () {
  94
+		computeCoverage ();
  95
+
  96
+		if (parent != null)
  97
+			parent.recomputeCoverage ();
  98
+	}
  99
+
  100
+	public void FilterItem (bool isFiltered) {
  101
+		if (filtered != isFiltered) {
  102
+			filtered = isFiltered;
  103
+			recomputeCoverage ();
  104
+		}
  105
+	}
  106
+
  107
+	public override string ToString () {
  108
+		return "" + GetType () + "(hit=" + hit + ", missed=" + missed + ")";
  109
+	}
  110
+}
  111
+}
481  CoverageModel.cs
... ...
@@ -0,0 +1,481 @@
  1
+
  2
+using System;
  3
+using System.Collections;
  4
+using System.Xml;
  5
+using System.IO;
  6
+using System.Reflection;
  7
+using System.Text.RegularExpressions;
  8
+
  9
+using Mono.CSharp.Debugger;
  10
+
  11
+namespace MonoCov {
  12
+
  13
+public class CoverageModel : CoverageItem {
  14
+
  15
+	private Hashtable namespaces;
  16
+	private Hashtable classes;
  17
+	private Hashtable sources;
  18
+
  19
+	private Hashtable loadedAssemblies;
  20
+	private Hashtable symbolFiles;
  21
+
  22
+	/**
  23
+	 * List of filters, which are strings
  24
+	 */
  25
+	private ArrayList filters;
  26
+
  27
+	public CoverageModel () {
  28
+		namespaces = new Hashtable ();
  29
+		classes = new Hashtable ();
  30
+		sources = new Hashtable ();
  31
+		filters = new ArrayList ();
  32
+	}
  33
+
  34
+	public Hashtable Classes {
  35
+		get {
  36
+			return classes;
  37
+		}
  38
+	}
  39
+
  40
+	public Hashtable Namespaces {
  41
+		get {
  42
+			return namespaces;
  43
+		}
  44
+	}
  45
+
  46
+	public void AddFilter (String pattern) {
  47
+		filters.Add (pattern);
  48
+	}
  49
+
  50
+	private bool IsFiltered (string name) {
  51
+
  52
+		name = name.Replace ("mscorlib", "corlib");
  53
+
  54
+		// Check positive filters first
  55
+		bool hasPositive = false;
  56
+		bool found = false;
  57
+		foreach (String pattern in filters) {
  58
+			if (pattern [0] == '+') {
  59
+				string p = pattern.Substring (1);
  60
+				if (name.IndexOf (p) != -1) {
  61
+					// Console.WriteLine ("FILTERED: " + regex + " -> " + name);
  62
+					found = true;
  63
+				}
  64
+				hasPositive = true;
  65
+			}
  66
+		}
  67
+		if (hasPositive && !found)
  68
+			return true;
  69
+
  70
+		foreach (String pattern in filters) {
  71
+			if (pattern [0] == '-') {
  72
+				string p = pattern.Substring (1);
  73
+				if (name.IndexOf (p) != -1) {
  74
+					// Console.WriteLine ("FILTERED: " + regex + " -> " + name);
  75
+					return true;
  76
+				}
  77
+			}
  78
+		}
  79
+		return false;
  80
+	}
  81
+
  82
+	private void LoadAssemblies (XmlDocument dom) {
  83
+		foreach (XmlNode n in dom.GetElementsByTagName ("assembly")) {
  84
+			string assemblyName = n.Attributes ["name"].Value;
  85
+			string guid = n.Attributes ["guid"].Value;
  86
+
  87
+			Assembly assembly = Assembly.Load (assemblyName);
  88
+
  89
+			if  (assembly.GetLoadedModules ()[0].Mono_GetGuid () !=
  90
+				 new Guid (guid)) {
  91
+				Console.WriteLine ("WARNING: Loaded version of assembly " + assembly + " is different from the version used to collect coverage data.");
  92
+			}
  93
+
  94
+			MonoSymbolFile symbolFile;
  95
+
  96
+			loadedAssemblies [assemblyName] = assembly;
  97
+
  98
+			Console.Write ("Reading symbols for " + assembly + " ...");
  99
+			symbolFile = MonoSymbolFile.ReadSymbolFile (assembly);
  100
+			if (symbolFile == null)
  101
+				Console.WriteLine (" (No symbols found)");
  102
+			else {
  103
+				symbolFiles [assembly] = symbolFile;
  104
+				Console.WriteLine (" (" + symbolFile.SourceCount + " files, " + symbolFile.MethodCount + " methods)");
  105
+			}
  106
+		}
  107
+	}		
  108
+
  109
+	private void LoadFilters (XmlDocument dom) {
  110
+		foreach (XmlNode n in dom.GetElementsByTagName ("filter")) {
  111
+			AddFilter (n.Attributes ["pattern"].Value);
  112
+		}
  113
+	}
  114
+
  115
+	public void ReadFromFile (string fileName) {
  116
+		namespaces = new Hashtable ();
  117
+		classes = new Hashtable ();
  118
+
  119
+		long begin = DateTime.Now.Ticks / 10000;
  120
+		long msec = DateTime.Now.Ticks / 10000;
  121
+		long msec2;
  122
+
  123
+		loadedAssemblies = new Hashtable ();
  124
+		symbolFiles = new Hashtable ();
  125
+
  126
+		XmlDocument dom = new XmlDocument ();
  127
+		Console.Write ("Loading " + fileName + "...");
  128
+		dom.Load (new XmlTextReader (new FileStream (fileName, FileMode.Open)));
  129
+		Console.WriteLine (" Done.");
  130
+
  131
+		msec2 = DateTime.Now.Ticks / 10000;
  132
+		Console.WriteLine ("XML Reading: " + (msec2 - msec) + " msec");
  133
+		msec = msec2;
  134
+
  135
+		LoadAssemblies (dom);
  136
+
  137
+		LoadFilters (dom);
  138
+
  139
+		msec2 = DateTime.Now.Ticks / 10000;
  140
+		Console.WriteLine ("Load assemblies: " + (msec2 - msec) + " msec");
  141
+		msec = msec2;
  142
+
  143
+		foreach (XmlNode n in dom.GetElementsByTagName ("method")) {
  144
+			string assemblyName = n.Attributes ["assembly"].Value;
  145
+			string className = n.Attributes ["class"].Value;
  146
+			string methodName = n.Attributes ["name"].Value;
  147
+			string token = n.Attributes ["token"].Value;
  148
+			string cov_info = n.FirstChild.Value;
  149
+			
  150
+			Assembly assembly = (Assembly)loadedAssemblies [assemblyName];
  151
+			MonoSymbolFile symbolFile = (MonoSymbolFile)symbolFiles [assembly];
  152
+
  153
+			if (symbolFile == null)
  154
+				continue;
  155
+
  156
+			Type t = assembly.GetType (className);
  157
+			if (t == null) {
  158
+				Console.WriteLine ("ERROR: Unable to resolve type " + className + " in " + assembly);
  159
+				return;
  160
+			}
  161
+
  162
+			ClassCoverageItem klass = ProcessClass (t);
  163
+
  164
+			MethodEntry entry = symbolFile.GetMethodByToken (Int32.Parse (token));
  165
+			MethodBase monoMethod = assembly.MonoDebugger_GetMethod (Int32.Parse (token));
  166
+
  167
+			ProcessMethod (monoMethod, entry, klass, methodName, cov_info);
  168
+		}
  169
+
  170
+		msec2 = DateTime.Now.Ticks / 10000;
  171
+		Console.WriteLine ("Process methods: " + (msec2 - msec) + " msec");
  172
+		msec = msec2;
  173
+
  174
+		// Add info for klasses for which we have no coverage
  175
+
  176
+		foreach (Assembly assembly in loadedAssemblies.Values) {
  177
+			foreach (Type t in assembly.GetTypes ()) {
  178
+				ProcessClass (t);
  179
+			}
  180
+		}
  181
+
  182
+		// Add info for methods for which we have no coverage
  183
+
  184
+		foreach (ClassCoverageItem klass in classes.Values) {
  185
+			foreach (MethodInfo mb in klass.type.GetMethods (BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Static|BindingFlags.Instance | BindingFlags.DeclaredOnly)) {
  186
+				MonoSymbolFile symbolFile = (MonoSymbolFile)symbolFiles [klass.type.Assembly];
  187
+				if (symbolFile == null)
  188
+					continue;
  189
+
  190
+				if (! klass.methodsByMethod.ContainsKey (mb)) {
  191
+					MethodEntry entry = symbolFile.GetMethod (mb);
  192
+					ProcessMethod (mb, entry, klass, mb.Name, null);
  193
+				}
  194
+			}
  195
+		}
  196
+
  197
+		msec2 = DateTime.Now.Ticks / 10000;
  198
+		Console.WriteLine ("Additional classes: " + (msec2 - msec) + " msec");
  199
+		msec = msec2;
  200
+
  201
+		// Compute coverage for all items
  202
+
  203
+		computeCoverage (true);
  204
+
  205
+		msec2 = DateTime.Now.Ticks / 10000;
  206
+		Console.WriteLine ("Compute coverage: " + (msec2 - msec) + " msec");
  207
+		msec = msec2;
  208
+
  209
+		Console.WriteLine ("All: " + (msec2 - begin) + " msec");
  210
+
  211
+		// Free memory
  212
+		symbolFiles = null;
  213
+	}
  214
+
  215
+	//
  216
+	// Computes the coverage of METHOD
  217
+	//
  218
+
  219
+	private char[] digits = "0123456789".ToCharArray ();
  220
+	private char[] ws = "\t\n ".ToCharArray ();
  221
+
  222
+	private int parsePositiveInteger (string s, int pos) {
  223
+		int n = 0;
  224
+
  225
+		while (s [pos] >= '0' && s [pos] <= '9'){
  226
+			n = n * 10 + (s [pos] - '0');
  227
+			pos ++;
  228
+		}
  229
+		return n;
  230
+	}
  231
+
  232
+	private void computeMethodCoverage (MethodCoverageItem method,
  233
+										LineNumberEntry[] lines,
  234
+										string cov_info) {
  235
+
  236
+		ClassCoverageItem klass = method.Class;
  237
+		SourceFileCoverageData source = klass.sourceFile;
  238
+
  239
+		source.AddMethod (method);
  240
+
  241
+		int nlines = method.endLine - method.startLine + 1;
  242
+
  243
+		int[] coverage = new int [nlines];
  244
+
  245
+		if (cov_info == null) {
  246
+			for (int i = 0; i < nlines; ++i)
  247
+				coverage [i] = 0;
  248
+		}
  249
+		else {
  250
+			for (int i = 0; i < nlines; ++i)
  251
+				coverage [i] = -1;
  252
+
  253
+			// Hand crafted parsing code since this is performance critical
  254
+			int pos = 0;
  255
+			int prev_offset = 0;
  256
+			while (pos < cov_info.Length) {
  257
+				int pos2 = cov_info.IndexOfAny (digits, pos);
  258
+				if (pos2 == -1)
  259
+					break;
  260
+				pos = cov_info.IndexOfAny (ws, pos2);
  261
+				if (pos == -1)
  262
+					break;
  263
+
  264
+				int offset = parsePositiveInteger (cov_info, pos2);
  265
+
  266
+				pos2 = cov_info.IndexOfAny (digits, pos);
  267
+				if (pos2 == -1)
  268
+					break;
  269
+				pos = cov_info.IndexOfAny (ws, pos2);
  270
+
  271
+				int count = parsePositiveInteger (cov_info, pos2);
  272
+
  273
+				offset += prev_offset;
  274
+				prev_offset = offset;
  275
+
  276
+				int line1;
  277
+				int line2;
  278
+
  279
+				bool found = GetSourceRangeFor (offset, method, lines, ref line1, ref line2);
  280
+
  281
+				/*
  282
+				  if (found && (entry.Name.IndexOf ("Find") != -1)) {
  283
+				  Console.WriteLine ("OFFSET: " + offset + " " + line1 + ":" + line2);
  284
+				  }
  285
+				*/
  286
+
  287
+				if (found) {
  288
+					for (int i = line1; i < line2 + 1; ++i)
  289
+						if ((i >= method.startLine) && (i <= method.endLine))
  290
+							if (coverage [i - method.startLine] < count)
  291
+								coverage [i - method.startLine] = count;
  292
+				}
  293
+			}
  294
+		}
  295
+
  296
+		int hit = 0;
  297
+		int missed = 0;
  298
+
  299
+		for (int i = 0; i < nlines; ++i) {
  300
+			int count = coverage [i];
  301
+			if (count > 0)
  302
+				hit ++;
  303
+			else if (count == 0)
  304
+				missed ++;
  305
+		}
  306
+
  307
+		method.setCoverage (hit, missed);
  308
+
  309
+		method.lineCoverage = coverage;
  310
+	}
  311
+
  312
+	//
  313
+	// Return a range of source lines which have something to do with OFFSET.
  314
+	//
  315
+	private bool GetSourceRangeFor (int offset, MethodCoverageItem method,
  316
+								   LineNumberEntry[] lines,
  317
+								   ref int startLine, ref int endLine) {
  318
+
  319
+		/**
  320
+		 * The line number info generated by mcs is pretty strange sometimes... :)
  321
+		 */
  322
+
  323
+		/**
  324
+		 * First, we split the range of IL offsets into consecutive blocks and
  325
+		 * identify the block which contains OFFSET. Then we identify the range
  326
+		 * of source lines which are mapped to this range by the line number 
  327
+		 * entries.
  328
+		 */
  329
+
  330
+		int beginOffset;
  331
+		int endOffset;
  332
+		int i;
  333
+
  334
+		for (i = 0; i < lines.Length; ++i) {
  335
+			if (offset >= lines [i].Offset)
  336
+				if (i == lines.Length - 1) {
  337
+					beginOffset = lines [i].Offset;
  338
+					endOffset = lines [i].Offset;
  339
+					break;
  340
+				}
  341
+				else if (offset < lines [i + 1].Offset) {
  342
+					beginOffset = lines [i].Offset;
  343
+					endOffset = lines [i + 1].Offset - 1;
  344
+					break;
  345
+				}
  346
+		}
  347
+
  348
+		/*
  349
+		if (method.Name.IndexOf ("Find") != -1) {
  350
+			Console.WriteLine ("OFFSET: " + offset + " " + beginOffset + " " + endOffset);
  351
+		}
  352
+		*/
  353
+
  354
+		if (i == lines.Length) {
  355
+			if (offset <= lines [0].Offset) {
  356
+				return false;
  357
+			}
  358
+			else {
  359
+				for (i = 0; i < lines.Length; ++i)
  360
+					Console.WriteLine (lines [i]);
  361
+				throw new Exception ("Unable to determine source range for offset " + offset + " in " + method.name);
  362
+			}
  363
+		}
  364
+		
  365
+		/* Find start line */
  366
+		for (i = 0; i < lines.Length; ++i)
  367
+			if (lines [i].Offset == beginOffset) {
  368
+				startLine = lines [i].Row;
  369
+				break;
  370
+			}
  371
+
  372
+		//	g_assert (i < num_line_numbers);
  373
+
  374
+		/* Find end line */
  375
+		if (lines.Length == 1)
  376
+			endLine = lines [0].Row;
  377
+		else {
  378
+			for (i = 0; i < lines.Length; ++i)
  379
+				if (i == lines.Length - 1) {
  380
+					endLine = lines [i].Row;
  381
+					break;
  382
+				}
  383
+				else if (lines [i + 1].Offset > endOffset) {
  384
+					endLine = lines [i + 1].Row - 1;
  385
+					break;
  386
+				}
  387
+		}
  388
+
  389
+		return true;
  390
+	}
  391
+
  392
+	private ClassCoverageItem ProcessClass (Type t) {
  393
+		string className = t.FullName;
  394
+		int nsindex = className.LastIndexOf (".");
  395
+		string namespace2;
  396
+		string scopedName;
  397
+		if (nsindex == -1) {
  398
+			namespace2 = "<GLOBAL>";
  399
+			scopedName = className;
  400
+		} else if (nsindex == 0) {
  401
+			namespace2 = "<GLOBAL>";
  402
+			scopedName = className.Substring (1);
  403
+		}
  404
+		else {
  405
+			namespace2 = className.Substring (0, nsindex);
  406
+			scopedName = className.Substring (nsindex + 1);
  407
+		}
  408
+
  409
+		// Create namespaces
  410
+		NamespaceCoverageItem ns = (NamespaceCoverageItem)namespaces [namespace2];
  411
+		if (ns == null) {
  412
+			string nsPrefix = "";
  413
+			foreach (String nsPart in namespace2.Split ('.')) {
  414
+				if (nsPrefix == "")
  415
+					nsPrefix = nsPart;
  416
+				else
  417
+					nsPrefix = nsPrefix + "." + nsPart;
  418
+				NamespaceCoverageItem ns2 = (NamespaceCoverageItem)namespaces [nsPrefix];
  419
+				if (ns2 == null) {
  420
+					if (ns == null)
  421
+						ns2 = new NamespaceCoverageItem (this, nsPrefix);
  422
+					else
  423
+						ns2 = new NamespaceCoverageItem (ns, nsPrefix);
  424
+					namespaces [nsPrefix] = ns2;
  425
+				}
  426
+				ns = ns2;
  427
+			}					
  428
+		}
  429
+
  430
+		ClassCoverageItem klass = (ClassCoverageItem)classes [className];
  431
+		if (klass == null) {
  432
+			klass = new ClassCoverageItem (ns);
  433
+			klass.name_space = namespace2;
  434
+			klass.name = scopedName;
  435
+			klass.type = t;
  436
+			klass.parent = ns;
  437
+
  438
+			klass.filtered = IsFiltered ("[" + t.Assembly + "]" + className);
  439
+			classes [className] = klass;
  440
+		}
  441
+
  442
+		return klass;
  443
+	}
  444
+
  445
+	private void ProcessMethod (MethodBase monoMethod, MethodEntry entry, ClassCoverageItem klass, string methodName, string cov_info) {
  446
+		if (entry == null)
  447
+			// Compiler generated, abstract method etc.
  448
+			return;
  449
+
  450
+		LineNumberEntry[] lines = entry.LineNumbers;
  451
+
  452
+		if (lines.Length == 0)
  453
+			return;
  454
+
  455
+		int start_line = lines [0].Row;
  456
+		int end_line = lines [lines.Length - 1].Row;
  457
+
  458
+		MethodCoverageItem method 
  459
+			= new MethodCoverageItem (klass, methodName);
  460
+
  461
+		method.startLine = start_line;
  462
+		method.endLine = end_line;
  463
+		method.filtered = IsFiltered ("[" + monoMethod.DeclaringType.Assembly + "]" + monoMethod.DeclaringType + "::" + monoMethod.Name);
  464
+		klass.methodsByMethod [monoMethod] = method;
  465
+
  466
+
  467
+		if (klass.sourceFile == null) {
  468
+			string sourceFile = entry.SourceFile.FileName;
  469
+
  470
+			SourceFileCoverageData source = (SourceFileCoverageData)sources [sourceFile];
  471
+			if (source == null) {
  472
+				source = new SourceFileCoverageData (sourceFile);
  473
+				sources [sourceFile] = source;
  474
+			}
  475
+			klass.sourceFile = source;
  476
+		}
  477
+			
  478
+		computeMethodCoverage (method, lines, cov_info);
  479
+	}
  480
+}
  481
+}
114  HtmlExporter.cs
... ...
@@ -0,0 +1,114 @@
  1
+
  2
+using System;
  3
+using System.Collections;
  4
+using System.Text;
  5
+using System.IO;
  6
+using System.Xml;
  7
+using System.Xml.Xsl;
  8
+using System.Xml.XPath;
  9
+
  10
+namespace MonoCov {
  11
+
  12
+public class HtmlExporter {
  13
+
  14
+	public class ProgressEventArgs {
  15
+		public CoverageItem item;
  16
+
  17
+		public string fileName;
  18
+
  19
+		public int pos;
  20
+
  21
+		public int itemCount;
  22
+
  23
+		public ProgressEventArgs (CoverageItem item, string fileName, 
  24
+								  int pos, int itemCount) {
  25
+			this.item = item;
  26
+			this.fileName = fileName;
  27
+			this.pos = pos;
  28
+			this.itemCount = itemCount;
  29
+		}
  30
+	}
  31
+
  32
+	public delegate void ProgressEventHandler (Object sender,
  33
+											   ProgressEventArgs e);
  34
+
  35
+
  36
+	public string DestinationDir;
  37
+
  38
+	public string StyleSheet;
  39
+
  40
+	public event ProgressEventHandler Progress;
  41
+
  42
+	private XmlTextWriter writer;
  43
+
  44
+	private CoverageModel model;
  45
+
  46
+	private static string DefaultStyleSheet = "style.xsl";
  47
+
  48
+	private int itemCount;
  49
+
  50
+	private int itemsProcessed;
  51
+
  52
+	private XslTransform transform;
  53
+
  54
+	//
  55
+	// Algorithm: export the data as XML, then transform it into HTML
  56
+	// using a stylesheet
  57
+	//
  58
+ 
  59
+	public void Export (CoverageModel model) {
  60
+
  61
+		this.model = model;
  62
+
  63
+		if (model.hit + model.missed == 0)
  64
+			return;
  65
+
  66
+		// Why doesn't NET has a Path.GetTempDirectoryName () method ?
  67
+		int index = 0;
  68
+		string tempDir;
  69
+		// Of course this is not safe but it doesn't matter
  70
+		while (true) {
  71
+			tempDir = Path.Combine (Path.Combine (Path.GetTempPath (), "monocov"), "" + index);
  72
+			if (! Directory.Exists (tempDir))
  73
+				break;
  74
+			index ++;
  75
+		}
  76
+		Directory.CreateDirectory (tempDir);
  77
+
  78
+		XmlExporter exporter = new XmlExporter ();
  79
+		exporter.StyleSheet = StyleSheet;
  80
+		exporter.DestinationDir = tempDir;
  81
+		exporter.Progress += new XmlExporter.ProgressEventHandler (progressListener);
  82
+		// Count items
  83
+		itemCount = 1 + model.Classes.Count + model.Namespaces.Count;
  84
+		itemsProcessed = 0;
  85
+
  86
+		exporter.Export (model);
  87
+
  88
+		Directory.Delete (tempDir, true);
  89
+	}
  90
+
  91
+	private void progressListener (object sender, XmlExporter.ProgressEventArgs e) {
  92
+		if (e.fileName != null) {
  93
+			string name = Path.GetFileName (e.fileName);
  94
+
  95
+			if (transform == null) {
  96
+				transform = new XslTransform();
  97
+				transform.Load(Path.Combine (Path.GetDirectoryName (e.fileName), "style.xsl"));
  98
+			}
  99
+
  100
+			try {
  101
+				XsltArgumentList args = new XsltArgumentList ();
  102
+				args.AddParam ("link-suffix", "", ".html");
  103
+				transform.Transform (new XPathDocument (new StreamReader (e.fileName)), args, new StreamWriter (Path.Combine (DestinationDir, name.Replace (".xml", ".html"))));
  104
+			}
  105
+			catch (Exception ex) {
  106
+				Console.WriteLine ("Error: Unable to transform '" + e.fileName + "': " + ex);
  107
+			}
  108
+		}
  109
+
  110
+		if (Progress != null)
  111
+			Progress (this, new ProgressEventArgs (e.item, e.fileName, e.pos, e.itemCount));
  112
+	}	
  113
+}
  114
+}
11  LICENSE
... ...
@@ -0,0 +1,11 @@
  1
+Copyright (c) 2002 Zoltan Varga
  2
+
  3
+This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
  4
+
  5
+Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
  6
+
  7
+    1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
  8
+
  9
+    2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
  10
+
  11
+    3. This notice may not be removed or altered from any source distribution.
30  MANIFEST
... ...
@@ -0,0 +1,30 @@
  1
+MANIFEST
  2
+README
  3
+LICENSE
  4
+ChangeLog
  5
+Makefile
  6
+CoverageItem.cs
  7
+NamespaceCoverageItem.cs
  8
+ClassCoverageItem.cs
  9
+MethodCoverageItem.cs
  10
+CoverageModel.cs
  11
+SourceFileCoverageData.cs
  12
+XmlExporter.cs
  13
+HtmlExporter.cs
  14
+style.xsl
  15
+trans.gif
  16
+gui/qt/MonoCov.cs
  17
+gui/qt/CoverageView.cs
  18
+gui/qt/SourceWindow.cs
  19
+gui/qt/FilterDialog.cs
  20
+gui/qt/filterdialog.ui
  21
+gui/gtk/MonoCov.cs
  22
+gui/gtk/CoverageView.cs
  23
+gui/gtk/monocov.glade
  24
+test.cs
  25
+symbols.cs
  26
+nunit-console.cs
  27
+coverage.c
  28
+Qt-monocov.dll
  29
+qtsharp.diff
  30
+corlib-tests.cov
72  Makefile
... ...
@@ -0,0 +1,72 @@
  1
+
  2
+PROJECTNAME = monocov
  3
+GUI = qt
  4
+MONO_ROOT = $(HOME)/mono-cvs/mono
  5
+
  6
+all: monocov.exe libmono-profiler-monocov.so symbols.exe nunit-console.exe
  7
+
  8
+ifeq ($(GUI), gtk)
  9
+GUI_SRCS = \
  10
+	gui/gtk/MonoCov.cs \
  11
+	gui/gtk/CoverageView.cs
  12
+GUI_LIBS = -r gtk-sharp.dll -r gdk-sharp.dll -r glib-sharp.dll -r glade-sharp.dll -r gnome-sharp.dll -r System.Drawing -resource:gui/gtk/monocov.glade,monocov.glade
  13
+else
  14
+GUI_SRCS = \
  15
+	gui/qt/MonoCov.cs \
  16
+	gui/qt/CoverageView.cs \
  17
+	gui/qt/SourceWindow.cs \
  18
+	gui/qt/FilterDialog.cs 
  19
+GUI_LIBS = -r Qt
  20
+endif
  21
+
  22
+SRCS = \
  23
+	CoverageItem.cs \
  24
+	NamespaceCoverageItem.cs \
  25
+	ClassCoverageItem.cs \
  26
+	MethodCoverageItem.cs \
  27
+	CoverageModel.cs \
  28
+	SourceFileCoverageData.cs \
  29
+	XmlExporter.cs \
  30
+	HtmlExporter.cs \
  31
+	$(GUI_SRCS)
  32
+
  33
+monocov.exe: $(SRCS) style.xsl
  34
+	mcs -g /target:exe /out:$@ -r Mono.CSharp.Debugger -r Mono.GetOptions $(GUI_LIBS) $(SRCS) -resource:style.xsl,style.xsl -resource:trans.gif,trans.gif
  35
+
  36
+symbols.exe: symbols.cs
  37
+	mcs -g /target:exe /out:$@ -r Mono.CSharp.Debugger symbols.cs
  38
+
  39
+nunit-console.exe: nunit-console.cs
  40
+	mcs -r NUnit.Framework -r Mono.GetOptions nunit-console.cs
  41
+
  42
+libmono-profiler-monocov.so: coverage.c
  43
+	$(CC) -g -I$(MONO_ROOT) `pkg-config --cflags glib-2.0` --shared -o $@ $^
  44
+
  45
+test:
  46
+	mcs -g test.cs
  47
+	./mono --profile=monocov:outfile=res.cov test.exe
  48
+
  49
+cortests:
  50
+	MONO_PATH=$(HOME)/mono-cvs/mcs/class/corlib/Test ./mono --profile=monocov:outfile=corlib-tests.cov,+[corlib] nunit-console.exe corlib_test.dll
  51
+
  52
+export-cortests:
  53
+	./monocov.exe --export-xml=export corlib-tests.cov
  54
+	tar cvzf corlib-tests.tar.gz export
  55
+
  56
+html-cortests:
  57
+	./monocov.exe --export-html=html-export corlib-tests.cov
  58
+	tar cvzf html-tests.tar.gz html-export
  59
+
  60
+#	MONO_PATH=$(HOME)/mono-cvs/mcs/class/corlib/Test ./mono --profile=monocov:outfile=corlib-tests.cov,-MonoTests,-NUnit,-[System nunit-console.exe -x Security corlib_test.dll
  61
+
  62
+hash-test:
  63
+	./mono --profile=monocov:+Hashtable hash-table.exe
  64
+
  65
+test-colorizer.exe: test-colorizer.cs SyntaxHighlighter.cs
  66
+	mcs -g /out:$@ $^
  67
+
  68
+clean:
  69
+	rm -f monocov.exe symbols.exe nunit-console.exe
  70
+
  71
+distrib:
  72
+	tar -cvhzf $(PROJECTNAME).tar.gz `cat MANIFEST` && DIRNAME=$(PROJECTNAME)-`date +%d-%b-%y` && rm -rf $$DIRNAME && mkdir $$DIRNAME && mv $(PROJECTNAME).tar.gz $$DIRNAME && cd $$DIRNAME && tar -xzf $(PROJECTNAME).tar.gz && rm $(PROJECTNAME).tar.gz && cd - && tar -cvzf $$DIRNAME.tar.gz $$DIRNAME && rm -rf $$DIRNAME
41  MethodCoverageItem.cs
... ...
@@ -0,0 +1,41 @@
  1
+
  2
+using System;
  3
+using System.Collections;
  4
+using Mono.CSharp.Debugger;
  5
+
  6
+namespace MonoCov {
  7
+
  8
+public class MethodCoverageItem : CoverageItem {
  9
+
  10
+	public string name;
  11
+	public int startLine;
  12
+	public int endLine;
  13
+
  14
+	public int[] lineCoverage;
  15
+
  16
+	public MethodCoverageItem (ClassCoverageItem parent, String name) : base (parent) {
  17
+		this.name = name;
  18
+	}
  19
+
  20
+	public ClassCoverageItem Class {
  21
+		get {
  22
+			return (ClassCoverageItem)parent;
  23
+		}
  24
+	}
  25
+
  26
+	public override bool IsLeaf {
  27
+		get {
  28
+			return true;
  29
+		}
  30
+	}
  31
+
  32
+	public string Name {
  33
+		get {
  34
+			return name;
  35
+		}
  36
+		set {
  37
+			name = value;
  38
+		}
  39
+	}
  40
+}
  41
+}
21  NamespaceCoverageItem.cs
... ...
@@ -0,0 +1,21 @@
  1
+
  2
+using System;
  3
+using System.Collections;
  4
+
  5
+namespace MonoCov {
  6
+
  7
+public class NamespaceCoverageItem : CoverageItem {
  8
+
  9
+	public string ns;
  10
+
  11
+	public NamespaceCoverageItem (CoverageItem parent, string ns) : base (parent) {
  12
+		this.ns = ns;
  13
+	}
  14
+
  15
+	public NamespaceCoverageItem ParentNamespace {
  16
+		get {
  17
+			return (NamespaceCoverageItem)parent;
  18
+		}
  19
+	}
  20
+}
  21
+}
158  README
... ...
@@ -0,0 +1,158 @@
  1
+
  2
+1. INTRODUCTION
  3
+---------------
  4
+
  5
+  MonoCov is a line coverage analysis program for mono. It can be used to 
  6
+display coverage data collected while running a .NET program. There are two
  7
+types of GUI interfaces, one implemented using Qt#, while the other is 
  8
+implemented using Gtk#. The Qt# version is more advanced.
  9
+
  10
+1.5 REQUIREMENTS
  11
+----------------
  12
+
  13
+The runtime parts are tested with the then current Mono CVS. The Qt# GUI 
  14
+requires a patched version of the Qt# library which is included in the 
  15
+distribution. This file is named Qt-monocov.dll, and the original Qt.dll should
  16
+be replace with this file. The upcoming Qt# 0.7.1 release fixes most of the
  17
+problems, so this won't be neccessary in the future.
  18
+
  19
+2. USAGE
  20
+--------
  21
+
  22
+2.1 COVERAGE DATA COLLECTION
  23
+----------------------------
  24
+
  25
+To produce coverage info for an .NET program, compile it with the -g
  26
+switch to generate debug information. After this, run the program as follows:
  27
+
  28
+$ ./mono --profile:monocov prog.exe
  29
+
  30
+This will produce a coverage data file called prog.exe.cov. You can run the
  31
+analyser program as follows:
  32
+
  33
+$ ./mono monocov.exe prog.exe.cov
  34
+
  35
+This will display the class hierarchy of the program with the corresponding
  36
+coverage information.
  37
+
  38
+It is also possible to filter the list of classes which need coverage data
  39
+generated. Filters are string which are applied agains the fully qualified 
  40
+names of classes, e.g. [assemblyname]classname. You can specify filters 
  41
+directly on the command line:
  42
+
  43
+$ ./mono --profile:monocov:-Security,-[System.Xml] prog.exe
  44
+
  45
+There are two types of filters: include filters, whose name begins with '+',
  46
+and exclude filters, whose name begins with '-'. Include filters are checked
  47
+before exclude filters.
  48
+
  49
+For example:
  50
+
  51
+$ ./mono --profile:monocov:+[corlib],-Hashtable prog.exe
  52
+
  53
+This will collect coverage info for all classes in corlib, except the ones
  54
+whose name contains 'Hashtable'.
  55
+
  56
+2.2 ANALYSIS
  57
+------------
  58
+
  59
+  The collected coverage data can be browsed using the monocov.exe program. 
  60
+This program will read the data file produced by the profiler module, and 
  61
+display its contents in a hierarchical fashion.
  62
+  It is also possible to export the contents of a data file into XML, which
  63
+can be viewed in an XSL capable browser like mozilla.
  64
+To export the data as XML, run monocov like this:
  65
+	monocov.exe --export-xml=<DEST DIR> <DATA FILE NAME>
  66
+  
  67
+The generated XML files use a default stylesheet which is a bit ugly. It would
  68
+be good if somebody could contribute a better one :)
  69
+
  70
+2.5 KNOWN BUGS
  71
+--------------
  72
+
  73
+Due to some memory management problems in Qt#, the GUI will most likely crash
  74
+on exit. It will also crash if the user tries to load a second data file.
  75
+
  76
+3. UTILITY PROGRAMS
  77
+-------------------
  78
+
  79
+There are two utility programs included with MonoCov:
  80
+
  81
+- symbols.exe: this program can be used to dump the line number information
  82
+  contained in an mcs generated assembly.
  83
+
  84
+- nunit-console.exe: this is a rewritten version of the original nunit console
  85
+  program. It has the following features:
  86
+  - easier to invoke: no stupid /assembly and /fixture arguments
  87
+  - ability to run test fixtures whose name matches a given pattern, like
  88
+    all the System.IO tests.
  89
+  - ability to exclude tests whose name matches a given pattern.
  90
+  - display of more detailed progress information.
  91
+
  92
+4. TODO
  93
+-------------
  94
+
  95
+- Add ability to run the program from inside the analyzer
  96
+- Add filters (globals/per program)
  97
+- Add HTML export option
  98
+- Handle nested classes more intelligently
  99
+
  100
+5. LICENSE
  101
+----------
  102
+
  103
+zlib/libpng.
  104
+
  105
+6. CONTACT
  106
+----------
  107
+
  108
+Zoltan Varga (vargaz@freemail.hu)
  109
+
  110
+RANDOM NOTES:
  111
+-------------
  112
+
  113
+- There is a bug in QString:~QString (): it should be
  114
+	if (this != Null)
  115
+		qt_del_QString (rawObject);
  116
+
  117
+- Mono finalizes QApplication before QMainWindow, leading to
  118
+  invalid memory read errors.
  119
+
  120
+- Mono frees the the string[] array passed to qt_new_QMainWindow, leading to
  121
+  invalid read errors.
  122
+
  123
+- DecimalTest2:.ctor () causes the register allocator to allocate more than
  124
+  2^16 registers, overflowing MonoInst->dreg.
  125
+
  126
+- Methods with strange debugging info:
  127
+	- System.Collections.Hashtable..cctor()
  128
+	- SortedList+SynchedSortedList::this [key]
  129
+	- SortedList+SynchedSortedList::Clear ()
  130
+	- System.Text.RegularExpressions.Capture
  131
+
  132
+- If trans.gif is missing from the export directory:
  133
+    - when viewing HTML, it doesn't matter
  134
+	- when viewing XML, it matters
  135
+
  136
+- How can a bar be created without using an image ?
  137
+
  138
+TODO:
  139
+- Add private paths used during data collection to the paths used to search for
  140
+  assemblies.
  141
+- add 'include' and 'exclude' to filters
  142
+- Implement merging of coverage results
  143
+- put namespaces above classes in the hierarchy
  144
+- handle missing source files
  145
+- QDesigner
  146
+- Scintilla
  147
+- html output (with XSLT)
  148
+- use Xml serialization in SyntaxHighlighter
  149
+- speed up test suite generation in nunit (or in mono)
  150
+- namespaces & filtering ???
  151
+- fix StackTrace tests
  152
+- add ability to exclude some appdomains (like the nunit main appdomain)
  153
+  so the tests will run faster.
  154
+- avoid instrumentation for instruction without side effects (like ldc.i4)
  155
+- why does the RSA tests take so much time -> because of entropy generation
  156
+- check in cryptography patches
  157
+- optimize reflection classes by caching the results of get_type_info etc.
  158
+- put monocov into mono CVS.
59  SourceFileCoverageData.cs
... ...
@@ -0,0 +1,59 @@
  1
+
  2
+
  3
+using System;
  4
+using System.Collections;
  5
+
  6
+namespace MonoCov {
  7
+
  8
+public class SourceFileCoverageData {
  9
+
  10
+	/// the name of the source file
  11
+	public string sourceFile;
  12
+
  13
+	/// contains line_number->count mappings
  14
+	/// count > 0    -> hit
  15
+	/// count == 0   -> missed
  16
+	/// count == -1  -> comment/declaration/whitespace/no info
  17
+	public int[] coverage;
  18
+
  19
+	// The list of methods which are part of this source file
  20
+	public ArrayList methods;
  21
+
  22
+	public SourceFileCoverageData (string sourceFile) {
  23
+		this.sourceFile = sourceFile;
  24
+	}
  25
+
  26
+	public void AddMethod (MethodCoverageItem method) {
  27
+		if (methods == null)
  28
+			methods = new ArrayList ();
  29
+		methods.Add (method);
  30
+	}
  31
+
  32
+	public int[] Coverage {
  33
+		get {
  34
+			if (coverage != null)
  35
+				return coverage;
  36
+
  37
+			if (methods == null)
  38
+				return new int [0];
  39
+
  40
+			int endLine = 0;
  41
+			foreach (MethodCoverageItem method in methods) {
  42
+				if (method.endLine > endLine)
  43
+					endLine = method.endLine;
  44
+			}
  45
+			coverage = new int [endLine + 1];
  46
+			for (int i = 0; i < coverage.Length; ++i)
  47
+				coverage [i] = -1;
  48
+
  49
+			foreach (MethodCoverageItem method in methods) {
  50
+				if (method.lineCoverage != null) {
  51
+					for (int i = 0; i < method.lineCoverage.Length; ++i)
  52
+						coverage [method.startLine + i] = method.lineCoverage [i];
  53
+				}
  54
+			}
  55
+			return coverage;
  56
+		}
  57
+	}
  58
+}
  59
+}
283  XmlExporter.cs
... ...
@@ -0,0 +1,283 @@
  1
+
  2
+using System;
  3
+using System.Collections;
  4
+using System.Text;
  5
+using System.IO;
  6
+using System.Xml;
  7
+
  8
+namespace MonoCov {
  9
+
  10
+public class XmlExporter {
  11
+
  12
+	public class ProgressEventArgs {
  13
+		public CoverageItem item;
  14
+
  15
+		public string fileName;
  16
+
  17
+		public int pos;
  18
+
  19
+		public int itemCount;
  20
+
  21
+		public ProgressEventArgs (CoverageItem item, string fileName,
  22
+								  int pos, int itemCount) {
  23
+			this.item = item;