private void _scrollToFunctionTextBox_KeyDown(object sender, KeyEventArgs e) { // ENTER pressed? if (e.Key == Key.Enter && !string.IsNullOrWhiteSpace(_scrollToFunctionTextBox.Text)) { // Find function with given name FunctionNode functionNode = _functionNodes.FirstOrDefault(fn => fn.FunctionName.Contains(_scrollToFunctionTextBox.Text)); if (functionNode != null) { // Scroll horizontally _renderPanelContainer.ScrollToHorizontalOffset(_renderPanel.LayoutTransform.Transform(new Point(functionNode.CenterXPosition, 0)).X); } } }
/// <summary> /// Creates a new entry arrow from the given source function node to the given destination function node. /// </summary> /// <param name="from">The source function node of the arrow.</param> /// <param name="to">The destination function node of the arrow.</param> /// <param name="positionY">The vertical position of the arrow.</param> /// <param name="traceFileEntry">The associated trace file entry.</param> /// <param name="traceFileEntryIndex">The index of this entry. This value should correspond to the mismatch index output by the comparison functions.</param> /// <param name="traceFileId">The ID of the associated trace file ({1, 2}). Used for determining the coloring of this entry.</param> public EntryArrow(FunctionNode from, FunctionNode to, double positionY, BranchEntry traceFileEntry, int traceFileEntryIndex, int traceFileId) : base() { // Save parameters PositionY = positionY; From = from; To = to; TraceFileEntry = traceFileEntry; TraceFileEntryIndex = traceFileEntryIndex; TraceFileId = traceFileId; // Disable hit testing IsHitTestVisible = false; // Create visuals _visual = CreateArrowVisual(false); _visualSelected = CreateArrowVisual(true); }
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(); }