Esempio n. 1
0
        private void _renderPanel_MouseMove(object sender, MouseEventArgs e)
        {
            // Update rendering offset if left mouse button is pressed
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                // Calculate new scroll offset
                Point mousePosition = e.GetPosition(_renderPanelContainer);
                _renderPanelContainer.ScrollToHorizontalOffset(_renderPanelContainer.HorizontalOffset - (mousePosition.X - _mouseClickLocation.X));
                _renderPanelContainer.ScrollToVerticalOffset(_renderPanelContainer.VerticalOffset - (mousePosition.Y - _mouseClickLocation.Y));
                _mouseClickLocation = mousePosition;

                // Update render data
                UpdateRenderedElements();
            }
            else if (_entryArrows != null)
            {
                // Get mouse position in render panel coordinate system
                Point mousePosition = e.GetPosition(_renderPanel);

                // Run through entry arrow list and test whether one was hit
                double     minimumYValueForMatch = mousePosition.Y - EntryArrow.ArrowTipSideLength; // Lower bound for Y filtering; offset should correspond to the vertical size of one arrow
                EntryArrow newlyHoveredElement   = null;
                foreach (EntryArrow entryArrow in _entryArrows)
                {
                    // Check Y coordinate for fast filtering
                    if (entryArrow.PositionY < minimumYValueForMatch)
                    {
                        continue;
                    }

                    // The list is sorted, so we can stop if the desired Y value is exceeded
                    if (entryArrow.PositionY > mousePosition.Y)
                    {
                        break;
                    }

                    // The entry is a candidate, now check X coordinate
                    double left;
                    double right;
                    if (entryArrow.From == entryArrow.To)
                    {
                        // ==
                        left  = entryArrow.From.CenterXPosition - EntryArrow.ArrowTipSideLength;
                        right = entryArrow.From.CenterXPosition + EntryArrow.ArrowTipSideLength;
                    }
                    else if (entryArrow.From.CenterXPosition < entryArrow.To.CenterXPosition)
                    {
                        // -->
                        left  = entryArrow.From.CenterXPosition;
                        right = entryArrow.To.CenterXPosition;
                    }
                    else
                    {
                        // <--
                        left  = entryArrow.To.CenterXPosition;
                        right = entryArrow.From.CenterXPosition;
                    }
                    if (mousePosition.X < left || right < mousePosition.X)
                    {
                        continue;
                    }

                    // Hit!
                    newlyHoveredElement = entryArrow;
                    break;
                }

                // Deselect currently hovered element
                if (_hoveredEntryArrow != null && _hoveredEntryArrow != newlyHoveredElement)
                {
                    // Deselect currently hovered element
                    _hoveredEntryArrow.Selected = false;
                    _renderPanel.Children.Remove(_hoveredEntryArrow);
                    _renderPanel.Children.Add(_hoveredEntryArrow);
                    _hoveredEntryArrow = null;

                    _debugLabel.Content = "";
                }

                // Show annotation
                if (newlyHoveredElement != _hoveredEntryArrow)
                {
                    // Select element
                    _hoveredEntryArrow          = newlyHoveredElement;
                    _hoveredEntryArrow.Selected = true;
                    _renderPanel.Children.Remove(_hoveredEntryArrow);
                    _renderPanel.Children.Add(_hoveredEntryArrow);

                    _debugLabel.Content = $"[{newlyHoveredElement.TraceFileEntryIndex}] {newlyHoveredElement.TraceFileEntry.ToString()}";
                }

                // Try to find diff area
                DiffArea hoveredDiffArea = null;
                foreach (DiffArea diffArea in _diffAreas)
                {
                    // Check Y coordinate
                    if (diffArea.PositionY <= mousePosition.Y && mousePosition.Y <= diffArea.PositionY + diffArea.Height)
                    {
                        // Hit
                        hoveredDiffArea = diffArea;
                        break;
                    }
                }

                // Show hover text if a diff area was found
                if (hoveredDiffArea == null)
                {
                    _resultLabel.Content = "";
                }
                else
                {
                    _resultLabel.Content = hoveredDiffArea.Description;
                }
            }
        }
Esempio n. 2
0
        private void _loadTraceFileButton_Click(object sender, RoutedEventArgs e)
        {
            // Check input first
            string mapFileName    = Properties.Settings.Default.MapFileName;
            string trace1FileName = Properties.Settings.Default.Trace1FileName;
            string trace2FileName = Properties.Settings.Default.Trace2FileName;

            if (string.IsNullOrWhiteSpace(trace2FileName))
            {
                trace2FileName = trace1FileName;
            }
            if (!File.Exists(mapFileName) || !File.Exists(trace1FileName) || !File.Exists(trace2FileName))
            {
                MessageBox.Show("File(s) not found.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            // Reset lists possibly containing data from previous loads
            _imageFileNames.Clear();
            _mapFiles.Clear();

            // Delete all drawable elements
            _functionNodes = new List <FunctionNode>();
            _functionNodeNameAnnotations = new List <AnnotationElement>();
            _entryArrows = new List <EntryArrow>();
            _diffAreas   = new List <DiffArea>();
            _renderPanel.Children.Clear();

            // Local function to create function nodes (called in several places)
            double nextNodeX = 20;

            FunctionNode CreateFunctionNode(string functionName)
            {
                // Create new node
                FunctionNode node = new FunctionNode(functionName, new Point(nextNodeX, 20));

                nextNodeX += node.Width;
                _functionNodes.Add(node);
                return(node);
            };

            // Regex for removing extensions from image names
            Regex imageNameRegex = new Regex("\\.(dll|exe)(\\..*)?", RegexOptions.Compiled | RegexOptions.IgnoreCase);

            // Load map files
            string mapFileImageName = System.IO.Path.GetFileNameWithoutExtension(mapFileName).ToLower();

            _mapFiles.Add(imageNameRegex.Replace(mapFileImageName, ""), new MapFile(mapFileName));

            // Load trace files
            _traceFile1 = new TraceFile(trace1FileName, _imageFileNames, 1);
            _traceFile1.CacheEntries();
            _traceFile2 = new TraceFile(trace2FileName, _imageFileNames, 1);
            _traceFile2.CacheEntries();

            // Local function to retrieve function metadata for a given address
            Dictionary <int, string> imageIdMapFileNameMapping        = new Dictionary <int, string>();
            Dictionary <int, Dictionary <ulong, FunctionNode> > nodes = new Dictionary <int, Dictionary <ulong, FunctionNode> >();

            FunctionNode GetFunctionNodeForBranchEntryOperand(int imageId, string imageName, ulong instructionAddress)
            {
                // Try to retrieve name of function
                string cleanedImageName = imageNameRegex.Replace(imageName, "");

                if (!imageIdMapFileNameMapping.ContainsKey(imageId))
                {
                    // Check whether map file exists
                    if (_mapFiles.ContainsKey(cleanedImageName.ToLower()))
                    {
                        imageIdMapFileNameMapping.Add(imageId, cleanedImageName.ToLower());
                    }
                    else
                    {
                        imageIdMapFileNameMapping.Add(imageId, null);
                    }
                    nodes.Add(imageId, new Dictionary <ulong, FunctionNode>());
                    nodes[imageId].Add(0, CreateFunctionNode(cleanedImageName));
                }
                string sourceMapFileName        = imageIdMapFileNameMapping[imageId];
                ulong  sourceInstructionAddress = instructionAddress - 0x1000; // TODO hardcoded -> correct?

                var(sourceFunctionAddress, sourceFunctionName) = (sourceMapFileName == null ? (0, "Unknown") : _mapFiles[sourceMapFileName].GetSymbolNameByAddress(sourceInstructionAddress));

                // Retrieve source function node
                if (!nodes[imageId].ContainsKey(sourceFunctionAddress))
                {
                    nodes[imageId].Add(sourceFunctionAddress, CreateFunctionNode(cleanedImageName + " ! " + sourceFunctionName));
                }
                return(nodes[imageId][sourceFunctionAddress]);
            };

            double CreateEntryArrow(IEnumerator <TraceEntry> traceFileEnumerator, double y, int index, int traceFileId)
            {
                // Retrieve entry and check its type
                traceFileEnumerator.MoveNext();
                TraceEntry entry = traceFileEnumerator.Current;

                if (entry.EntryType != TraceEntryTypes.Branch)
                {
                    return(0);
                }

                // Draw if taken
                BranchEntry branchEntry = (BranchEntry)entry;

                if (branchEntry.Taken)
                {
                    // Get function nodes of first entry
                    FunctionNode sourceFunctionNode1      = GetFunctionNodeForBranchEntryOperand(branchEntry.SourceImageId, branchEntry.SourceImageName, branchEntry.SourceInstructionAddress);
                    FunctionNode destinationFunctionNode1 = GetFunctionNodeForBranchEntryOperand(branchEntry.DestinationImageId, branchEntry.DestinationImageName, branchEntry.DestinationInstructionAddress);

                    // Add line
                    _entryArrows.Add(new EntryArrow(sourceFunctionNode1, destinationFunctionNode1, y, branchEntry, index, traceFileId));
                    return(EntryArrow.ArrowTipSideLength + 4);
                }
                return(0);
            };

            // Compare trace files and create diff
            TraceFileDiff diff                 = new TraceFileDiff(_traceFile1, _traceFile2);
            double        nextEntryY           = 60;
            var           traceFile1Enumerator = _traceFile1.Entries.GetEnumerator();
            var           traceFile2Enumerator = _traceFile2.Entries.GetEnumerator();
            List <Tuple <double, double, Brush, string> > requestedDiffAreas = new List <Tuple <double, double, Brush, string> >();

            foreach (var diffEntry in diff.RunDiff())
            {
                // Save Y offset of first entry
                double baseY = nextEntryY;

                // Draw branch entries for trace
                int diffEntryCount1      = diffEntry.EndLine1 - diffEntry.StartLine1;
                int diffEntryCount2      = diffEntry.EndLine2 - diffEntry.StartLine2;
                int commonDiffEntryCount = Math.Min(diffEntryCount1, diffEntryCount2);
                for (int i = 0; i < commonDiffEntryCount; ++i)
                {
                    // Draw main entry
                    nextEntryY += CreateEntryArrow(traceFile1Enumerator, nextEntryY, diffEntry.StartLine1 + i, 1);

                    // If there are differences, draw corresponding other entry; move the enumerator anyway
                    if (!diffEntry.Equal)
                    {
                        nextEntryY += CreateEntryArrow(traceFile2Enumerator, nextEntryY, diffEntry.StartLine2 + i, 2);
                    }
                    else
                    {
                        traceFile2Enumerator.MoveNext();
                    }
                }

                // Draw remaining entries from longer sequence
                if (diffEntryCount1 > commonDiffEntryCount)
                {
                    for (int i = commonDiffEntryCount; i < diffEntryCount1; ++i)
                    {
                        nextEntryY += CreateEntryArrow(traceFile1Enumerator, nextEntryY, diffEntry.StartLine1 + i, 1);
                    }
                }
                else if (diffEntryCount2 > commonDiffEntryCount)
                {
                    for (int i = commonDiffEntryCount; i < diffEntryCount2; ++i)
                    {
                        nextEntryY += CreateEntryArrow(traceFile2Enumerator, nextEntryY, diffEntry.StartLine2 + i, 2);
                    }
                }

                // Differences? => Schedule
                if (!diffEntry.Equal)
                {
                    // Make sure this diff area is visible even if it does not contain branch entries
                    requestedDiffAreas.Add(new Tuple <double, double, Brush, string>(baseY - 2, nextEntryY + 2, Brushes.LightPink, $"DIFF   A: {diffEntry.StartLine1}-{diffEntry.EndLine1 - 1} vs. B: {diffEntry.StartLine2}-{diffEntry.EndLine2 - 1}"));
                    nextEntryY += 6;
                }
            }

            // Show message if files match
            if (!requestedDiffAreas.Any())
            {
                MessageBox.Show("The compared files match.", "Trace diff", MessageBoxButton.OK, MessageBoxImage.Information);
            }

            // Set draw panel dimensions
            _renderPanel.Width  = nextNodeX;
            _renderPanel.Height = nextEntryY;

            // Draw diff areas in the background
            FunctionNode lastFunctionNode = _functionNodes.LastOrDefault();

            if (lastFunctionNode != null)
            {
                foreach (var reqDiffArea in requestedDiffAreas)
                {
                    // Create diff area
                    DiffArea diffArea = new DiffArea(lastFunctionNode.Position.X + lastFunctionNode.Width, reqDiffArea.Item2 - reqDiffArea.Item1, reqDiffArea.Item3, reqDiffArea.Item4, reqDiffArea.Item1);
                    Canvas.SetLeft(diffArea, 0);
                    Canvas.SetTop(diffArea, reqDiffArea.Item1);
                    _diffAreas.Add(diffArea);
                    _renderPanel.Children.Add(diffArea);
                }
            }

            // Draw function nodes above the diff areas
            foreach (var node in _functionNodes)
            {
                // Insert function node
                node.GenerateVisual(nextEntryY);
                _renderPanel.Children.Add(node);
                Canvas.SetLeft(node, node.Position.X);
                Canvas.SetTop(node, node.Position.Y);

                // Create annotation node to display function name independent of scrolling
                // The vertical position will be updated in another place
                AnnotationElement annotationElement = new AnnotationElement(node.FunctionName);
                Canvas.SetLeft(annotationElement, node.CenterXPosition - annotationElement.TopLeft.X + 2);
                _functionNodeNameAnnotations.Add(annotationElement);
                _renderPanel.Children.Add(annotationElement);
            }

            // Finally draw the entry arrows in the foreground
            foreach (var entry in _entryArrows)
            {
                // Prepare coordinates
                Canvas.SetLeft(entry, entry.From.CenterXPosition);
                Canvas.SetTop(entry, entry.PositionY);
            }

            // Update render data
            _renderPanel.LayoutTransform = new ScaleTransform(_zoom, _zoom);
            UpdateRenderedElements();
        }