예제 #1
0
        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);
        }
예제 #2
0
        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);
        }
예제 #3
0
        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;
        }
예제 #4
0
        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
        }
예제 #5
0
        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));
            }
        }
예제 #6
0
        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);
        }
예제 #7
0
        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 };
        }
예제 #8
0
        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();
        }
예제 #9
0
        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;
        }
예제 #10
0
        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++;
        }
예제 #11
0
        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;
        }
예제 #12
0
        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;
                }
            }
        }
예제 #13
0
        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);
            }
        }