public CallItem(FunctionCall call, NodeModel node, bool perCall) { Call = call; Node = node; Text = node.AppendClassName(); if (call == null) return; if (call.StillInside > 0 ) Text += " (" + call.StillInside.ToString() + " Still Inside)"; SubItems.Add(call.TotalHits.ToString()); Hits = call.TotalHits; Inside = call.TotalTimeInsideDest; Outside = call.TotalTimeOutsideDest; if (Hits == 0) return; if (perCall) { Inside /= Hits; Outside /= Hits; } SubItems.Add(Utilities.TicksToString(Inside)); SubItems.Add(Utilities.TicksToString(Outside)); Total = Inside + Outside; // show last cal info string callInfo = ""; try { var lastParams = call.LastParameters; if (lastParams != null) callInfo += "Params: " + string.Join(", ", lastParams.Select(p => (p == null) ? "<null>" : p.ToString()).ToArray()); } catch (Exception x) { throw new Exception("AAA"); } object lastReturnValue = null; try { lastReturnValue = call.LastReturnValue; // avoid thread from changing value out from under us } catch (Exception x) { throw new Exception("b1" + x.Message); } try { if (lastReturnValue != null) callInfo += " Return: "; } catch (Exception x) { throw new Exception("b2" + x.Message); } try { if (lastReturnValue != null) callInfo += lastReturnValue.ToString(); } catch (Exception x) { throw new Exception("b3" + x.Message); } SubItems.Add(callInfo); }
internal void AddFunctionCall(ref SharedDictionary<FunctionCall> map, int nodeId, FunctionCall call) { if (map == null) map = new SharedDictionary<FunctionCall>(1); if (!map.Contains(nodeId)) map.Add(nodeId, call); }
public static FunctionCall CreateNewCall(int hash, int sourceID, XNodeIn dest) { var call = new FunctionCall() { ID = hash, Source = sourceID, Destination = dest.ID }; CallMap.Add(hash, call); // add link to node that its been called dest.AddFunctionCall(ref dest.CalledIn, sourceID, call); var source = Nodes[sourceID]; source.AddFunctionCall(ref source.CallsOut, dest.ID, call); //***** new location of create of class to class add function call CreateLayerCall(source, dest); if (Remote != null) foreach (var client in Remote.SyncClients) lock(client.NewCalls) client.NewCalls.Add(new Tuple<int, int>(sourceID, dest.ID)); CallChange = true; if (!ClassTracking) return call; var srcNode = Nodes[call.Source]; if (srcNode == null) return call; var sourceClass = GetContainingClass(srcNode) as XNodeIn; var destClass = GetContainingClass(dest) as XNodeIn; if (sourceClass == destClass) return call; if (destClass.ObjType != XObjType.Class || sourceClass.ObjType != XObjType.Class) { LogError("parent not class type, {0} and {1}", destClass.ObjType, sourceClass.ObjType); return call; } hash = sourceClass.ID * FunctionCount + destClass.ID; call.ClassCallHash = hash; //LogError("Adding to class map {0} -> {1} with hash {2}", sourceClass.ID, destClass.ID, hash); var classCall = new FunctionCall() { ID = hash, Source = sourceClass.ID, Destination = destClass.ID }; ClassCallMap.Add(hash, classCall); destClass.AddFunctionCall(ref destClass.CalledIn, sourceClass.ID, classCall); sourceClass.AddFunctionCall(ref sourceClass.CallsOut, destClass.ID, classCall); return call; }
public static void TrackClassCall(FunctionCall functionCall, int thread) { // save class hash in call to avoid lookup FunctionCall call; if(!ClassCallMap.TryGetValue(functionCall.ClassCallHash, out call)) return; call.Hit = ShowTicks; if (RemoteViewer) return; call.TotalHits++; if (!RemoteViewer && !call.ThreadIDs.Contains(thread)) call.ThreadIDs.Add(thread); // remote infers class hit and thread by the function to function call }
private static void CheckCreateInit(XNodeIn sourceClass, XNodeIn classNode) { int hash = sourceClass.ID * FunctionCount + classNode.ID; FunctionCall call; if (!InitMap.TryGetValue(hash, out call)) { //LogError("Adding to init map {0} -> {1} with hash {2}", sourceClass.ID, node.ID, hash); call = new FunctionCall() { ID = hash, Source = sourceClass.ID, Destination = classNode.ID }; InitMap.Add(hash, call); if (classNode.InitsBy == null) classNode.InitsBy = new HashSet<int>(); if (sourceClass.InitsOf == null) sourceClass.InitsOf = new HashSet<int>(); classNode.InitsBy.Add(sourceClass.ID); sourceClass.InitsOf.Add(classNode.ID); if (Remote != null) foreach (var client in Remote.SyncClients) lock(client.Inits) client.Inits.Add(new Tuple<int, int>(sourceClass.ID, classNode.ID)); } }
public void CreateStackItem(int nodeID, FunctionCall call, long startTick, bool isMethod, bool ThreadlineEnabled) { Pos++; Handle = Thread.CurrentThread; // not sure if id can stay the same while handle object changes if thread id resurrected if (Pos >= XRay.MaxStack) return; var newItem = new StackItem() { NodeID = nodeID, Call = call, StartTick = startTick, Depth = Pos }; if (isMethod) Stack[Pos] = newItem; else { newItem.EndTick = startTick; Pos--; } if (!ThreadlineEnabled) return; AddStackItem(newItem); }
private void AddStaticCall(XNodeOut source, XNodeOut dest) { if (!Build.StaticAnalysis) return; int hash = XRay.PairHash(source.ID, dest.ID); CallMap[hash] = new FunctionCall() { ID = hash, Source = source.ID, Destination = dest.ID }; }
private void RecompileMethod(XNodeOut classNode, TypeDefinition classDef, MethodDefinition method) { XNodeOut methodNode = classNode.AddMethod(method); // return if (method.ReturnType != null) { var returnNode = SetClassDependency(classNode, method.ReturnType); if (returnNode != null) methodNode.ReturnID = returnNode.ID; } // params for (int i = 0; i < method.Parameters.Count; i++) { var p = method.Parameters[i]; if (methodNode.ParamIDs == null) { methodNode.ParamIDs = new int[method.Parameters.Count]; methodNode.ParamNames = new string[method.Parameters.Count]; } var paramNode = SetClassDependency(classNode, p.ParameterType); methodNode.ParamIDs[i] = paramNode.ID; methodNode.ParamNames[i] = p.Name; } if (method.Body == null) return; // local vars foreach (var local in method.Body.Variables) SetClassDependency(classNode, local.VariableType); // expands branches/jumps to support adddresses > 255 // possibly needed if injecting code into large functions // OptimizeMacros at end of the function re-optimizes method.Body.SimplifyMacros(); methodNode.Lines = method.Body.Instructions.Count; var processor = method.Body.GetILProcessor(); for (int i = 0; i < method.Body.Instructions.Count; i++) { var instruction = method.Body.Instructions[i]; // record method exited if (Build.TrackFlow && instruction.OpCode == OpCodes.Ret) { instruction.OpCode = OpCodes.Nop; // any 'goto return' will go to here so method exit gets logged first i = TrackMethodExit(method, method, methodNode, processor, i); AddInstruction(method, ++i, processor.Create(OpCodes.Ret)); } // if we're tracking calls to non-xrayed assemblies else if (instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Calli || instruction.OpCode == OpCodes.Callvirt) { var call = instruction.Operand as MethodReference; if (call == null) { Debug.WriteLine("Unable to track not xrayed: " + instruction.Operand.ToString()); continue; } SetClassDependency(classNode, call.ReturnType); SetClassDependency(classNode, call.DeclaringType); foreach (var p in call.Parameters) SetClassDependency(classNode, p.ParameterType); var calledRef = GetClassRef(call.DeclaringType); var calledNode = calledRef.AddMethod(call); // add call pair from this method to that method AddStaticCall(methodNode, calledNode); /*if( TrackExternal && !(method.Name == "Finalize" && method.DeclaringType.Namespace == "System") && (instruction.Operand as MethodReference).DeclaringType.Namespace != EnterMethodRef.DeclaringType.Namespace )*/ if (Build.TrackExternal && calledNode.External && calledRef.Name != "XRay") // !(method.Name == "Finalize" && method.DeclaringType.Namespace == "System")) { if (method.Name == ".cctor" && call.Name == "GetTypeFromHandle") continue; // call added to cctor by xray calledNode.Lines = 1; bool isConstrained = false; // in function is prefixed by .constrained, wrap enter/exit around those 2 lines int offset = 0; if (i > 0 && method.Body.Instructions[i - 1].OpCode == OpCodes.Constrained) { i -= 1; offset = 1; isConstrained = true; } // put stuff below in here so instructions dont get messed up var oldPos = method.Body.Instructions[i]; int pos = i; // if this is an external call and we want to track the parameters, then we have to wrap the function // cannot wrap .ctor because .ctor can only be called from within a .ctor if (Build.TrackParameters && call.HasParameters && call.Name != ".ctor") { // wrap call in a new function with the same parameters and return type as the original call, we do this // because it's maybe impossible to build the object[] of parameters from the current stack because it's unknown // in a wrapped function we can access the arguments easily to build the object[] and pass to method enter var wrappedName = string.Format("{0}_{1}_XRay{2}", call.DeclaringType.Name, call.Name, UniqueEnterSig++); var resolvedCall = ResolveGenericMethod(call); var wrapFunc = new MethodDefinition(wrappedName, new Mono.Cecil.MethodAttributes(), resolvedCall.ReturnType); // the wrapper can be static because we pass the called function's declaring type into the wrapper wrapFunc.IsPrivate = true; wrapFunc.IsStatic = true; wrapFunc.HasThis = false; wrapFunc.IsHideBySig = true; if (call.HasThis) { // calling functions against a value type is done by calling against its address eg DateTime.AddMinutes() if (call.DeclaringType.IsValueType) wrapFunc.Parameters.Add(new ParameterDefinition(new ByReferenceType(call.DeclaringType))); else wrapFunc.Parameters.Add(new ParameterDefinition(call.DeclaringType)); } foreach (var p in resolvedCall.Parameters) wrapFunc.Parameters.Add(p); // write body of method var wrapProcessor = wrapFunc.Body.GetILProcessor(); TrackMethodEnterParams(wrapFunc, calledNode.ID, wrapProcessor, call.HasThis); // load 'this' and arguemnts for(int x = 0; x < wrapFunc.Parameters.Count; x++) wrapFunc.Body.Instructions.Add(wrapProcessor.Create(OpCodes.Ldarg, x)); if (isConstrained) { // test with oldfashionedfun with 'no parameter' filter above turned off var inst = method.Body.Instructions[pos]; wrapFunc.Body.Instructions.Add(wrapProcessor.Create(inst.OpCode, inst.Operand as TypeReference)); } // call original function wrapFunc.Body.Instructions.Add(wrapProcessor.Create(instruction.OpCode, call)); // return wrapFunc.Body.Instructions.Add(wrapProcessor.Create(OpCodes.Ret)); classDef.Methods.Add(wrapFunc); // replace current call instruction with call to copy method var wrapRef = new MethodReference(wrapFunc.Name, wrapFunc.ReturnType); foreach (var parameter in wrapFunc.Parameters) wrapRef.Parameters.Add(parameter); if (classDef.HasGenericParameters) { // have to add arguments to declaring type manually for some reason var genericClassDef = new GenericInstanceType(classDef); foreach (var parameter in classDef.GenericParameters) genericClassDef.GenericArguments.Add(parameter); wrapRef.DeclaringType = genericClassDef; } else wrapRef.DeclaringType = classDef; method.Body.Instructions[pos++].OpCode = OpCodes.Nop; if(isConstrained) method.Body.Instructions[pos++].OpCode = OpCodes.Nop; // sets the actual call to nop AddInstruction(method, pos, processor.Create(OpCodes.Call, wrapRef)); // not incrementing pos because enter function takes un-inc'd pos as a param // really need to go back through and standardize pos setting } else { // wrap the call with enter and exit, because enter to an external method may cause // an xrayed method to be called, we want to track the flow of that process AddInstruction(method, pos++, processor.Create(OpCodes.Ldc_I4, calledNode.ID)); AddInstruction(method, pos++, processor.Create(OpCodes.Call, MethodEnterRef)); // method pos += offset; } pos = TrackMethodExit(method, call, calledNode, processor, pos); var newPos = method.Body.Instructions[i]; // get new instruction at original position, inserting stuff changed it UpdateExceptionHandlerPositions(method, oldPos, newPos); i = pos; // loop end will add 1 putting us right after last added function } } else if (Build.TrackFields && (instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld || instruction.OpCode == OpCodes.Ldfld || instruction.OpCode == OpCodes.Ldflda || instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldsflda)) { var fieldDef = instruction.Operand as FieldReference; var classRef = GetClassRef(fieldDef.DeclaringType); var fieldRef = classRef.AddField(fieldDef); // some times volitile prefixes set/get field int offset = 0; if (i > 0 && method.Body.Instructions[i - 1].OpCode == OpCodes.Volatile) { i--; offset = 1; } AddInstruction(method, i, processor.Create(OpCodes.Ldc_I4, fieldRef.ID)); if (instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld) { AddInstruction(method, i + 1, processor.Create(OpCodes.Call, SetFieldRef)); AddStaticCall(methodNode, fieldRef); } else { AddInstruction(method, i + 1, processor.Create(OpCodes.Call, LoadFieldRef)); if(XRay.FieldGetLeftToRight) AddStaticCall(fieldRef, methodNode); else AddStaticCall(methodNode, fieldRef); } i = i + 2 + offset; // skip instuction added, and field itself, end of loop will iterate this again // Debug.WriteLine("{0} in Module: {1}, Namespace: {2}, Class: {3}, Name: {4}, Type: {5}", instruction.OpCode, fieldDef.DeclaringType.Scope.Name, namespaces, className, fieldName, fieldType); } else if (instruction.OpCode == OpCodes.Newobj) { var newObj = instruction.Operand as MethodReference; var sourceClass = XRay.GetContainingClass(methodNode); var destClass = SetClassDependency(classNode, newObj.DeclaringType); if (Build.StaticAnalysis) { int hash = XRay.PairHash(sourceClass.ID, destClass.ID); InitMap[hash] = new FunctionCall() { ID = hash, Source = sourceClass.ID, Destination = destClass.ID }; } } /* Still not really working - goal - to get side by side wpf apps to work * else if (instruction.OpCode == OpCodes.Ldstr && !Build.ReplaceOriginal) { // rename Pack URIs in WPF so resources can be found with an XRay.. namespace // ex: "/MyApp;component/views/aboutview.xaml" -> "/XRay.MyApp;component/views/aboutview.xaml" var packUri = instruction.Operand as String; foreach (var file in XRayedFiles) packUri = packUri.Replace("/" + file.AssemblyName + ";", "/XRay." + file.AssemblyName + ";"); //packUri = packUri.Replace(file.AssemblyName, "XRay." + file.AssemblyName); instruction.Operand = packUri; }*/ } // end iterating through instructions // record function was entered if (Build.TrackFunctions) { if (Build.TrackParameters && method.HasParameters) TrackMethodEnterParams(method, methodNode.ID, processor, false); else { AddInstruction(method, 0, processor.Create(OpCodes.Ldc_I4, methodNode.ID)); AddInstruction(method, 1, processor.Create(OpCodes.Call, MethodEnterRef)); } } // record catches if (Build.TrackFlow) foreach (var handler in method.Body.ExceptionHandlers) { if (handler.HandlerType != ExceptionHandlerType.Catch) continue; int i = method.Body.Instructions.IndexOf(handler.HandlerStart); AddInstruction(method, i, processor.Create(OpCodes.Ldc_I4, methodNode.ID)); AddInstruction(method, i + 1, processor.Create(OpCodes.Call, MethodCatchRef)); var oldPos = handler.HandlerStart; var newPos = method.Body.Instructions[i]; UpdateExceptionHandlerPositions(method, oldPos, newPos); } method.Body.OptimizeMacros(); }
public CallItem(FunctionCall call, NodeModel node, bool perCall) { Call = call; Node = node; Text = node.AppendClassName(); if (call == null) return; if (call.StillInside > 0 ) Text += " (" + call.StillInside.ToString() + " Still Inside)"; SubItems.Add(call.TotalHits.ToString()); Hits = call.TotalHits; Inside = call.TotalTimeInsideDest; Outside = call.TotalTimeOutsideDest; if (Hits == 0) return; if (perCall) { Inside /= Hits; Outside /= Hits; } SubItems.Add(Utilities.TicksToString(Inside)); SubItems.Add(Utilities.TicksToString(Outside)); Total = Inside + Outside; }
public static void TrackClassCall(XNodeIn node, int source, int thread) { var srcNode = Nodes[source]; if (srcNode == null) return; var sourceClass = GetContainingClass(srcNode) as XNodeIn; var destClass = GetContainingClass(node) as XNodeIn; if (sourceClass == destClass) return; if (destClass.ObjType != XObjType.Class || sourceClass.ObjType != XObjType.Class) { LogError("parent not class type, {0} and {1}", destClass.ObjType, sourceClass.ObjType); return; } int hash = sourceClass.ID * FunctionCount + destClass.ID; FunctionCall call; if (!ClassCallMap.TryGetValue(hash, out call)) { //LogError("Adding to class map {0} -> {1} with hash {2}", sourceClass.ID, destClass.ID, hash); call = new FunctionCall() { Source = sourceClass.ID, Destination = destClass.ID }; ClassCallMap.Add(hash, call); destClass.AddFunctionCall(ref destClass.CalledIn, sourceClass.ID, call); sourceClass.AddFunctionCall(ref sourceClass.CallsOut, destClass.ID, call); } call.ThreadIDs.Add(thread); call.Hit = ShowTicks; call.TotalHits++; }
public static FunctionCall CreateNewCall(int hash, int sourceID, XNodeIn dest) { var call = new FunctionCall() { Source = sourceID, Destination = dest.ID }; CallMap.Add(hash, call); // add link to node that its been called dest.AddFunctionCall(ref dest.CalledIn, sourceID, call); var source = Nodes[sourceID]; source.AddFunctionCall(ref source.CallsOut, dest.ID, call); //***** new location of create of class to class add function call CreateLayerCall(source, dest); CallChange = true; return call; }
public void AddStackItem(int nodeID, FunctionCall call, long startTick, bool isMethod, bool ThreadlineEnabled) { Pos++; Handle = Thread.CurrentThread; if (Pos >= XRay.MaxStack) return; var newItem = new StackItem() { NodeID = nodeID, Call = call, StartTick = startTick, Depth = Pos }; if (isMethod) Stack[Pos] = newItem; else { newItem.EndTick = startTick; Pos--; } if (!ThreadlineEnabled) return; // dont over write items in timeline that haven't ended yet while (true) { ThreadlinePos++; if (ThreadlinePos >= Threadline.Length) ThreadlinePos = 0; var overwrite = Threadline[ThreadlinePos]; if (overwrite == null || overwrite.EndTick != 0) { Threadline[ThreadlinePos] = newItem; break; } } }
private static void TrackInit(XNodeIn node) { int thread = 0; if (ThreadTracking) thread = Thread.CurrentThread.ManagedThreadId; ThreadFlow flow; if (!FlowMap.TryGetValue(thread, out flow)) { LogError("Init Error 1"); return; } if (flow.Pos < 0) { LogError("Init Error 2"); return; } XNodeIn sourceClass = null; // travel up stack until we find something we know and mark that as the source of the init for (int i = flow.Pos; i >= 0; i--) { int source = flow.Stack[i].NodeID; var srcNode = Nodes[source]; if (srcNode == null) continue; sourceClass = GetContainingClass(srcNode) as XNodeIn; if (sourceClass != null && sourceClass != node) break; } if (sourceClass == null) { LogError("Init Error 3"); return; } if (node.ObjType != XObjType.Class) { LogError("Init Error 4"); return; } if (sourceClass == node) { LogError("Init Error 5 " + node.Name); //? class could create itself... return; } // link int hash = sourceClass.ID * FunctionCount + node.ID; FunctionCall call; if (!InitMap.TryGetValue(hash, out call)) { //LogError("Adding to init map {0} -> {1} with hash {2}", sourceClass.ID, node.ID, hash); call = new FunctionCall() { Source = sourceClass.ID, Destination = node.ID }; InitMap.Add(hash, call); if (node.InitsBy == null) node.InitsBy = new HashSet<int>(); if (sourceClass.InitsOf == null) sourceClass.InitsOf = new HashSet<int>(); node.InitsBy.Add(sourceClass.ID); sourceClass.InitsOf.Add(node.ID); } }