5 people like it.
Like the snippet!
Simple exception analysis
A simple script to list the exceptions that can be thrown during the method invocation (both caught and uncaught).
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
|
open Mono.Reflection
/// check for the throw opcode preceded with newobj
let getThrownException (instr: Instruction) =
let prev = instr.Previous
if instr.OpCode = OpCodes.Throw && prev <> null && prev.OpCode = OpCodes.Newobj then
let newType = (prev.Operand :?> ConstructorInfo).ReflectedType
if typeof<System.Exception>.IsAssignableFrom newType then
Some (instr, newType.FullName)
else None
else None
let inline instructionInfo (m: MethodBase) (i, t) comment =
sprintf "%s->%s: %A%s" m.ReflectedType.FullName m.Name t comment
let inline hasCallOpcode (i: Instruction) =
i.OpCode = OpCodes.Call || i.OpCode = OpCodes.Callvirt || i.OpCode = OpCodes.Calli
/// add some additional information about the exceptions
/// e.g. the value of message parameter for the standard ArgumentException
(get exception resource)
let addComment (i: Instruction) =
let comment =
match i.Operand with
| :? MethodBase as mi when i.OpCode=OpCodes.Call && mi.Name="ThrowArgumentException"
&& mi.GetParameters().Length>0 ->
let msg = tryGetExceptionResource i.Previous.Operand
if Option.isSome msg then
sprintf " (param: %A)" msg.Value
else ""
| _ -> ""
i, comment
/// add exceptions to dictionary
let inline (<~) (dict: Dictionary<_, HashSet<_>>) (m, items) = (...)
/// perform analysis for a method with given detalization parameters
let analyzeExceptions (m: MethodBase) (detalization, maxDepth) =
printfn ">> Analyze %s (%s):" m.Name m.ReflectedType.FullName
let exnsFound = Dictionary<_,HashSet<_>>()
let rec check d (m: MethodBase) comment =
if m = null || m.GetMethodBody() = null then HashSet<_>()
else
let tab = String.replicate d " "
let inline printDetails() = (...)
if exnsFound.ContainsKey m then printDetails()
else
let instructions = m.GetInstructions()
// exceptions in the method body
let exns =
instructions
|> Seq.map getThrownException
|> Seq.filter Option.isSome
|> Seq.map (fun e -> instructionInfo m e.Value comment)
exnsFound <~ (m, exns)
if d < detalization - 1 then printDetails()
// exceptions in inner calls
if d < maxDepth then
let innerExceptions =
instructions
|> Seq.filter hasCallOpcode
|> Seq.map addComment
|> Seq.collect (fun (i, c) -> check (d+1) (i.Operand :?> MethodBase) c)
exnsFound <~ (m, innerExceptions)
if d = detalization - 1 then printDetails()
exnsFound.[m]
check 0 m "" |> (fun i -> printfn "Summary:"; i) |> Seq.iter (printfn " %s")
(Example: get a couple of methods to analyze)
analyzeExceptions methods.["ToDictionary"] (2, 5) (output)
analyzeExceptions methods.["ThrowException"] (0, 5) (output)
|
namespace Mono
namespace Mono.Reflection
val getThrownException : instr:Instruction -> (Instruction * string) option
Full name: Script.getThrownException
check for the throw opcode preceded with newobj
val instr : Instruction
type Instruction =
member Next : Instruction with get, set
member Offset : int
member OpCode : OpCode
member Operand : obj with get, set
member Previous : Instruction with get, set
member Size : int
member ToString : unit -> string
Full name: Mono.Reflection.Instruction
val prev : Instruction
property Instruction.Previous: Instruction
property Instruction.OpCode: OpCode
type OpCodes =
static val Nop : OpCode
static val Break : OpCode
static val Ldarg_0 : OpCode
static val Ldarg_1 : OpCode
static val Ldarg_2 : OpCode
static val Ldarg_3 : OpCode
static val Ldloc_0 : OpCode
static val Ldloc_1 : OpCode
static val Ldloc_2 : OpCode
static val Ldloc_3 : OpCode
...
Full name: System.Reflection.Emit.OpCodes
field OpCodes.Throw
field OpCodes.Newobj
val newType : System.Type
property Instruction.Operand: obj
type ConstructorInfo =
inherit MethodBase
member Equals : obj:obj -> bool
member GetHashCode : unit -> int
member Invoke : parameters:obj[] -> obj + 1 overload
member MemberType : MemberTypes
static val ConstructorName : string
static val TypeConstructorName : string
Full name: System.Reflection.ConstructorInfo
val typeof<'T> : System.Type
Full name: Microsoft.FSharp.Core.Operators.typeof
namespace System
Multiple items
type Exception =
new : unit -> Exception + 2 overloads
member Data : IDictionary
member GetBaseException : unit -> Exception
member GetObjectData : info:SerializationInfo * context:StreamingContext -> unit
member GetType : unit -> Type
member HelpLink : string with get, set
member InnerException : Exception
member Message : string
member Source : string with get, set
member StackTrace : string
...
Full name: System.Exception
--------------------
System.Exception() : unit
System.Exception(message: string) : unit
System.Exception(message: string, innerException: exn) : unit
union case Option.Some: Value: 'T -> Option<'T>
property System.Type.FullName: string
union case Option.None: Option<'T>
val instructionInfo : m:MethodBase -> i:'a * t:'b -> comment:string -> string
Full name: Script.instructionInfo
val m : MethodBase
type MethodBase =
inherit MemberInfo
member Attributes : MethodAttributes
member CallingConvention : CallingConventions
member ContainsGenericParameters : bool
member Equals : obj:obj -> bool
member GetGenericArguments : unit -> Type[]
member GetHashCode : unit -> int
member GetMethodBody : unit -> MethodBody
member GetMethodImplementationFlags : unit -> MethodImplAttributes
member GetParameters : unit -> ParameterInfo[]
member Invoke : obj:obj * parameters:obj[] -> obj + 1 overload
...
Full name: System.Reflection.MethodBase
val i : 'a
val t : 'b
val comment : string
val sprintf : format:Printf.StringFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
property MemberInfo.ReflectedType: System.Type
property MemberInfo.Name: string
val hasCallOpcode : i:Instruction -> bool
Full name: Script.hasCallOpcode
val i : Instruction
field OpCodes.Call
field OpCodes.Callvirt
field OpCodes.Calli
let types = System.Reflection.Assembly.GetAssembly(typeof<System.ArgumentException>).GetTypes()
let tryGetExceptionResource (prm: obj) =
types
|> Seq.tryFind (fun t -> t.FullName = "System.ExceptionResource")
|> Option.bind (fun e ->Some (System.Enum.ToObject(e, prm)))
val addComment : i:Instruction -> Instruction * string
Full name: Script.addComment
val mi : MethodBase
MethodBase.GetParameters() : ParameterInfo []
val msg : obj option
val tryGetExceptionResource : prm:obj -> obj option
Full name: Script.tryGetExceptionResource
module Option
from Microsoft.FSharp.Core
val isSome : option:'T option -> bool
Full name: Microsoft.FSharp.Core.Option.isSome
property Option.Value: obj
val dict : Dictionary<'a,HashSet<'b>>
Multiple items
type Dictionary<'TKey,'TValue> =
new : unit -> Dictionary<'TKey, 'TValue> + 5 overloads
member Add : key:'TKey * value:'TValue -> unit
member Clear : unit -> unit
member Comparer : IEqualityComparer<'TKey>
member ContainsKey : key:'TKey -> bool
member ContainsValue : value:'TValue -> bool
member Count : int
member GetEnumerator : unit -> Enumerator<'TKey, 'TValue>
member GetObjectData : info:SerializationInfo * context:StreamingContext -> unit
member Item : 'TKey -> 'TValue with get, set
...
nested type Enumerator
nested type KeyCollection
nested type ValueCollection
Full name: System.Collections.Generic.Dictionary<_,_>
--------------------
Dictionary() : unit
Dictionary(capacity: int) : unit
Dictionary(comparer: IEqualityComparer<'TKey>) : unit
Dictionary(dictionary: IDictionary<'TKey,'TValue>) : unit
Dictionary(capacity: int, comparer: IEqualityComparer<'TKey>) : unit
Dictionary(dictionary: IDictionary<'TKey,'TValue>, comparer: IEqualityComparer<'TKey>) : unit
Multiple items
type HashSet<'T> =
new : unit -> HashSet<'T> + 3 overloads
member Add : item:'T -> bool
member Clear : unit -> unit
member Comparer : IEqualityComparer<'T>
member Contains : item:'T -> bool
member CopyTo : array:'T[] -> unit + 2 overloads
member Count : int
member ExceptWith : other:IEnumerable<'T> -> unit
member GetEnumerator : unit -> Enumerator<'T>
member GetObjectData : info:SerializationInfo * context:StreamingContext -> unit
...
nested type Enumerator
Full name: System.Collections.Generic.HashSet<_>
--------------------
HashSet() : unit
HashSet(comparer: IEqualityComparer<'T>) : unit
HashSet(collection: IEnumerable<'T>) : unit
HashSet(collection: IEnumerable<'T>, comparer: IEqualityComparer<'T>) : unit
val m : 'a
val items : seq<'b>
let set = if dict.ContainsKey m then dict.[m]
else
let s = HashSet<_>() in dict.Add(m, s); s
Seq.iter (set.Add >> ignore) items
val analyzeExceptions : m:MethodBase -> detalization:int * maxDepth:int -> unit
Full name: Script.analyzeExceptions
perform analysis for a method with given detalization parameters
val detalization : int
val maxDepth : int
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val exnsFound : Dictionary<MethodBase,HashSet<string>>
val check : (int -> MethodBase -> string -> HashSet<string>)
val d : int
MethodBase.GetMethodBody() : MethodBody
val tab : string
module String
from Microsoft.FSharp.Core
val replicate : count:int -> str:string -> string
Full name: Microsoft.FSharp.Core.String.replicate
val printDetails : (unit -> unit)
printfn "%s>> %s (%s)" tab m.Name m.ReflectedType.FullName
let detExns = exnsFound.[m]
if detExns.Count > 0 then detExns |> Seq.iter (printfn "\t%s%s" tab)
Dictionary.ContainsKey(key: MethodBase) : bool
val instructions : IList<Instruction>
(extension) MethodBase.GetInstructions() : IList<Instruction>
val exns : seq<string>
module Seq
from Microsoft.FSharp.Collections
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>
Full name: Microsoft.FSharp.Collections.Seq.map
val filter : predicate:('T -> bool) -> source:seq<'T> -> seq<'T>
Full name: Microsoft.FSharp.Collections.Seq.filter
val e : (Instruction * string) option
property Option.Value: Instruction * string
val innerExceptions : seq<string>
val collect : mapping:('T -> #seq<'U>) -> source:seq<'T> -> seq<'U>
Full name: Microsoft.FSharp.Collections.Seq.collect
val c : string
val i : HashSet<string>
val iter : action:('T -> unit) -> source:seq<'T> -> unit
Full name: Microsoft.FSharp.Collections.Seq.iter
open System.Linq
type T() =
let dict = Dictionary<_,_>()
member x.ToDictionary (arr: _[]) = arr.ToDictionary(id, string)
member x.ThrowException() = failwith "exception here"
let methods = typeof<T>.GetMethods() |> Seq.filter (fun m -> m.DeclaringType = typeof<T> && m.GetMethodBody() <> null)
|> Seq.map (fun m -> m.Name, m) |> Map.ofSeq
val methods : Map<string,MethodInfo>
Full name: Script.methods
>> Analyze ToDictionary (FSI_0003+T):
>> ToDictionary (FSI_0003+T)
>> ArgumentNull (System.Linq.Error)
>> ArgumentNull (System.Linq.Error)
>> ToDictionary (System.Linq.Enumerable)
System.ThrowHelper->ThrowArgumentNullException: "System.ArgumentNullException"
System.ThrowHelper->ThrowArgumentException: "System.ArgumentException" (param: Argument_AddingDuplicate)
Summary:
System.ThrowHelper->ThrowArgumentNullException: "System.ArgumentNullException"
System.ThrowHelper->ThrowArgumentException: "System.ArgumentException" (param: Argument_AddingDuplicate)
>> Analyze ThrowException (FSI_0003+T):
Summary:
FSI_0003+T->ThrowException: "System.Exception"
More information