示例#1
0
        /// <summary>
        /// Encodes all entries of the given trace file as 64-bit integers. This implies a loss of information.
        /// The least significant 4 bits of each entry will contain the entry type (directly casted from <see cref="TraceEntryTypes"/>).
        /// </summary>
        /// <param name="traceFile">The trace file to be encoded.</param>
        /// <returns>A list of encoded trace entries.</returns>
        public static IEnumerable <long> EncodeTraceEntries(TraceFile traceFile)
        {
            // Initialize stack memory allocation block
            AllocationData stackMemory = new AllocationData
            {
                AllocationLineNumber = 0,
                FreeLineNumber       = traceFile.EntryCount, // Never freed
                StartAddress         = traceFile.StackPointerMin,
                EndAddress           = traceFile.StackPointerMax
            };

            // Run through entries
            int line = 0;
            SortedList <int, AllocationData> allocs = new SortedList <int, AllocationData>();

            foreach (TraceEntry entry in traceFile.Entries)
            {
                // Encode type right away
                ulong enc = (ulong)entry.EntryType;
                switch (entry.EntryType)
                {
                case TraceEntryTypes.Allocation:
                {
                    // Save allocation data
                    AllocationEntry allocationEntry = (AllocationEntry)entry;
                    allocs[line] = new AllocationData()
                    {
                        AllocationLineNumber = line,
                        FreeLineNumber       = traceFile.EntryCount, // First assume as never freed, is overwritten as soon as a matching free is encountered
                        StartAddress         = allocationEntry.Address,
                        EndAddress           = allocationEntry.Address + allocationEntry.Size
                    };

                    // Encode allocation size
                    enc |= (ulong)allocationEntry.Size << 4;
                    break;
                }

                case TraceEntryTypes.Free:
                {
                    // Find corresponding allocation block to mark as freed
                    FreeEntry freeEntry       = (FreeEntry)entry;
                    var       allocCandidates = allocs.Where(a => a.Key <= line && a.Value.FreeLineNumber >= line && a.Value.StartAddress == freeEntry.Address);
                    if (!allocCandidates.Any())
                    {
                        // TODO Error handling (same problem as in TraceFileComparer)
                        //Debug.WriteLine($"Warning! Processing line {line}: Skipped free() without any matching allocation block.");
                    }
                    else
                    {
                        // Mark block as freed
                        AllocationData allocData = allocCandidates.First().Value;
                        allocData.FreeLineNumber = line;
                    }

                    // Not encoded
                    break;
                }

                case TraceEntryTypes.ImageMemoryRead:
                {
                    // Encode instruction and address offset
                    ImageMemoryReadEntry imageMemoryReadEntry = (ImageMemoryReadEntry)entry;
                    enc |= imageMemoryReadEntry.InstructionAddress << 4;
                    enc |= (ulong)imageMemoryReadEntry.MemoryAddress << 32;
                    break;
                }

                case TraceEntryTypes.ImageMemoryWrite:
                {
                    // Encode instruction and address offset
                    ImageMemoryWriteEntry imageMemoryWriteEntry = (ImageMemoryWriteEntry)entry;
                    enc |= imageMemoryWriteEntry.InstructionAddress << 4;
                    enc |= (ulong)imageMemoryWriteEntry.MemoryAddress << 32;
                    break;
                }

                case TraceEntryTypes.AllocMemoryRead:
                {
                    // Find related allocation block
                    // Stack or heap?
                    AllocMemoryReadEntry allocMemoryReadEntry = (AllocMemoryReadEntry)entry;
                    AllocationData       allocData            = stackMemory;
                    if (allocMemoryReadEntry.MemoryAddress < stackMemory.StartAddress || stackMemory.EndAddress < allocMemoryReadEntry.MemoryAddress)
                    {
                        // Heap?
                        var allocCandidates = allocs.Where(a => a.Key <= line && a.Value.FreeLineNumber >= line && a.Value.StartAddress <= allocMemoryReadEntry.MemoryAddress && allocMemoryReadEntry.MemoryAddress <= a.Value.EndAddress);
                        if (!allocCandidates.Any())
                        {
                            // TODO error handling - caused by missed malloc()
                            //Program.Log($"Error: Could not find allocation block for [{line}] {allocMemoryReadEntry.ToString()}\n", Program.LogLevel.Error);
                            Debug.WriteLine($"Error: Could not find allocation block for [{line}] {allocMemoryReadEntry.ToString()}");
                            break;
                        }
                        else
                        {
                            allocData = allocCandidates.Last().Value;
                        }
                    }

                    // Encode instruction address, the size of the allocation block, and the offset
                    enc |= allocMemoryReadEntry.InstructionAddress << 4;
                    enc |= (allocMemoryReadEntry.MemoryAddress - allocData.StartAddress) << 32;
                    //enc |= (allocData.EndAddress - allocData.StartAddress) << 48; // Only catches sizes less than 64 KB (16 bit address space)
                    break;
                }

                case TraceEntryTypes.AllocMemoryWrite:
                {
                    // Find related allocation block
                    // Stack or heap?
                    AllocMemoryWriteEntry allocMemoryWriteEntry = (AllocMemoryWriteEntry)entry;
                    AllocationData        allocData             = stackMemory;
                    if (allocMemoryWriteEntry.MemoryAddress < stackMemory.StartAddress || stackMemory.EndAddress < allocMemoryWriteEntry.MemoryAddress)
                    {
                        // Heap?
                        var allocCandidates = allocs.Where(a => a.Key <= line && a.Value.FreeLineNumber >= line && a.Value.StartAddress <= allocMemoryWriteEntry.MemoryAddress && allocMemoryWriteEntry.MemoryAddress <= a.Value.EndAddress);
                        if (!allocCandidates.Any())
                        {
                            // TODO error handling - caused by missed malloc()
                            //Program.Log($"Error: Could not find allocation block for [{line}] {allocMemoryWriteEntry.ToString()}\n", Program.LogLevel.Error);
                            Debug.WriteLine($"Error: Could not find allocation block for [{line}] {allocMemoryWriteEntry.ToString()}");
                            break;
                        }
                        else
                        {
                            allocData = allocCandidates.Last().Value;
                        }
                    }

                    // Encode instruction address, the size of the allocation block, and the offset
                    enc |= allocMemoryWriteEntry.InstructionAddress << 4;
                    enc |= (allocMemoryWriteEntry.MemoryAddress - allocData.StartAddress) << 32;
                    //enc |= (allocData.EndAddress - allocData.StartAddress) << 48; // Only catches sizes less than 64 KB (16 bit address space)
                    break;
                }

                case TraceEntryTypes.Branch:
                {
                    // Encode branch source and destination image offsets, and the "taken" flag
                    BranchEntry branchEntry = (BranchEntry)entry;
                    enc |= branchEntry.SourceInstructionAddress << 4;
                    enc |= (ulong)branchEntry.DestinationInstructionAddress << 32;
                    enc |= (ulong)(branchEntry.Taken ? 1 : 0) << 63;
                    break;
                }
                }
                yield return(unchecked ((long)enc));

                ++line;
            }
        }
示例#2
0
        /// <summary>
        /// Executes the comparison and returns whether the two traces have identical memory access patterns.
        /// </summary>
        /// <returns></returns>
        public bool Compare()
        {
            // Run linearily through trace entries and compare them
            // If the traces have different length, use the shorter one as reference
            // This should not lead to wrong results: The shorter trace won't just randomly end, instead there has to be some kind of branch mismatch before
            // Keep track of currently allocated datablocks
            AllocationData stackMemory = new AllocationData() // StartAddress = Top of stack, EndAddress = Bottom of stack
            {
                StartAddress1 = TraceFile1.StackPointerMin,
                StartAddress2 = TraceFile2.StackPointerMin,
                EndAddress1   = TraceFile1.StackPointerMax,
                EndAddress2   = TraceFile2.StackPointerMax
            };
            int totalEntries         = Math.Min(TraceFile1.EntryCount, TraceFile2.EntryCount);
            var traceFile1Enumerator = TraceFile1.Entries.GetEnumerator();
            var traceFile2Enumerator = TraceFile2.Entries.GetEnumerator();

            for (int i = 0; i < totalEntries; ++i)
            {
                // Retrieve entries
                traceFile1Enumerator.MoveNext();
                traceFile2Enumerator.MoveNext();
                TraceEntry e1 = traceFile1Enumerator.Current;
                TraceEntry e2 = traceFile2Enumerator.Current;

                // The entries should have the same type
                // If not, something must have gone very wrong: Differing branches are detected, so we should not run into differing instructions
                if (e1.EntryType != e2.EntryType)
                {
                    Result = ComparisonResults.ExecutionFlow_DifferentType;
                    ComparisonErrorLine  = i;
                    ComparisonErrorItems = new Tuple <TraceEntry, TraceEntry>(e1, e2);
                    if (PrintResults)
                    {
                        PrintComparisonResult($"Entries #{i} have different type (probably some bigger error occured?).", false,
                                              $"Trace 1: {e1.ToString()}",
                                              $"Trace 2: {e2.ToString()}");
                    }
                    return(false);
                }

                // Act depending on entry types
                switch (e1.EntryType)
                {
                // Possible leakages:
                // - Different branch targets
                // - In one execution the branch is taken, in the other one not
                case TraceEntryTypes.Branch:
                {
                    // Cast entries
                    BranchEntry branch1 = (BranchEntry)e1;
                    BranchEntry branch2 = (BranchEntry)e2;

                    // Compare targets
                    if (branch1.DestinationImageId != branch2.DestinationImageId || branch1.DestinationInstructionAddress != branch2.DestinationInstructionAddress)
                    {
                        Result = ComparisonResults.ExecutionFlow_DifferentBranchTarget;
                        ComparisonErrorLine  = i;
                        ComparisonErrorItems = new Tuple <TraceEntry, TraceEntry>(e1, e2);
                        if (PrintResults)
                        {
                            PrintComparisonResult($"Entries #{i}: Different branch target: <{branch1.DestinationImageName}+{branch1.DestinationInstructionAddress.ToString("X8")}> vs. <{branch2.DestinationImageName}+{branch2.DestinationInstructionAddress.ToString("X8")}>", false,
                                                  $"Trace 1: {e1.ToString()}",
                                                  $"Trace 2: {e2.ToString()}");
                        }
                        return(false);
                    }

                    // Compare "taken"
                    if (branch1.Taken != branch2.Taken)
                    {
                        if (branch1.Taken)
                        {
                            Result = ComparisonResults.ExecutionFlow_BranchTakenIn1;
                            ComparisonErrorLine  = i;
                            ComparisonErrorItems = new Tuple <TraceEntry, TraceEntry>(e1, e2);
                            if (PrintResults)
                            {
                                PrintComparisonResult($"Entries #{i}: Branch to <{branch1.DestinationImageName}+{branch1.DestinationInstructionAddress.ToString("X8")}> taken in trace 1, but not in trace 2.", false,
                                                      $"Trace 1: {e1.ToString()}",
                                                      $"Trace 2: {e2.ToString()}");
                            }
                        }
                        else
                        {
                            Result = ComparisonResults.ExecutionFlow_BranchTakenIn2;
                            ComparisonErrorLine  = i;
                            ComparisonErrorItems = new Tuple <TraceEntry, TraceEntry>(e1, e2);
                            if (PrintResults)
                            {
                                PrintComparisonResult($"Entries #{i}: Branch to <{branch1.DestinationImageName}+{branch1.DestinationInstructionAddress.ToString("X8")}> not taken in trace 1, but in trace 2.", false,
                                                      $"Trace 1: {e1.ToString()}",
                                                      $"Trace 2: {e2.ToString()}");
                            }
                        }
                        return(false);
                    }

                    // OK
                    break;
                }

                // Store allocation metadata
                // Possible leakages:
                // - The allocation size might differ
                case TraceEntryTypes.Allocation:
                {
                    // Cast entries
                    AllocationEntry alloc1 = (AllocationEntry)e1;
                    AllocationEntry alloc2 = (AllocationEntry)e2;

                    // Compare sizes
                    if (alloc1.Size != alloc2.Size)
                    {
                        Result = ComparisonResults.MemoryAccess_DifferentAllocationSize;
                        ComparisonErrorLine  = i;
                        ComparisonErrorItems = new Tuple <TraceEntry, TraceEntry>(e1, e2);
                        if (PrintResults)
                        {
                            PrintComparisonResult($"Entries #{i}: Allocation size differs: {alloc1.Size} vs. {alloc2.Size}", false,
                                                  $"Trace 1: {e1.ToString()}",
                                                  $"Trace 2: {e2.ToString()}");
                        }
                        return(false);
                    }

                    // Save allocation data
                    _allocs[alloc1.Address] = new AllocationData()
                    {
                        StartAddress1 = alloc1.Address,
                        EndAddress1   = alloc1.Address + alloc1.Size,
                        StartAddress2 = alloc2.Address,
                        EndAddress2   = alloc2.Address + alloc2.Size
                    };

                    // OK
                    break;
                }

                // Remove allocation metadata from list
                case TraceEntryTypes.Free:
                {
                    // Cast entries
                    FreeEntry free1 = (FreeEntry)e1;
                    FreeEntry free2 = (FreeEntry)e2;

                    // Make sure the frees apply to the same datablock
                    if (!_allocs.TryGetValue(free1.Address, out AllocationData freeCandidate))
                    {
                        /*Result = ComparisonResults.MemoryAccess_FreedBlockNotFound;
                         * ComparisonErrorLine = i;
                         * PrintComparisonResult($"Entries #{i}: Cannot find freed allocation block for execution trace 1.", false,
                         *  $"Trace 1: {e1.ToString()}",
                         *  $"Trace 2: {e2.ToString()}");
                         * // TODO This line is triggered in the Alloc/Free prefix, when one malloc() call was missed
                         * return false;*/
                    }
                    else if (freeCandidate.StartAddress2 != free2.Address)
                    {
                        Result = ComparisonResults.MemoryAccess_FreedBlockNotMatching;
                        ComparisonErrorLine  = i;
                        ComparisonErrorItems = new Tuple <TraceEntry, TraceEntry>(e1, e2);
                        if (PrintResults)
                        {
                            PrintComparisonResult($"Entries #{i}: Freed allocation block of execution trace 1 does not match freed block of execution trace 2.", false,
                                                  $"Trace 1: {e1.ToString()}",
                                                  $"Trace 2: {e2.ToString()}");
                        }
                        return(false);
                    }
                    else
                    {
                        // OK, remove allocation block
                        _allocs.Remove(free1.Address);
                    }
                    break;
                }

                // Possible leakages:
                // -> Different access offset in image
                case TraceEntryTypes.ImageMemoryRead:
                {
                    // Cast entries
                    ImageMemoryReadEntry read1 = (ImageMemoryReadEntry)e1;
                    ImageMemoryReadEntry read2 = (ImageMemoryReadEntry)e2;

                    // Compare relative access offsets
                    if (read1.MemoryAddress != read2.MemoryAddress)
                    {
                        Result = ComparisonResults.MemoryAccess_DifferentImageMemoryReadOffset;
                        ComparisonErrorLine  = i;
                        ComparisonErrorItems = new Tuple <TraceEntry, TraceEntry>(e1, e2);
                        if (PrintResults)
                        {
                            PrintComparisonResult($"Entries #{i}: Memory read offsets in image differ: {read1.MemoryImageName}+{read1.MemoryAddress.ToString("X")} vs. {read2.MemoryImageName}+{read2.MemoryAddress.ToString("X")}", false,
                                                  $"Trace 1: {e1.ToString()}",
                                                  $"Trace 2: {e2.ToString()}");
                        }
                        return(false);
                    }

                    // OK
                    break;
                }

                // Possible leakages:
                // -> Different access offset in image
                case TraceEntryTypes.ImageMemoryWrite:
                {
                    // Cast entries
                    ImageMemoryWriteEntry write1 = (ImageMemoryWriteEntry)e1;
                    ImageMemoryWriteEntry write2 = (ImageMemoryWriteEntry)e2;

                    // Compare relative access offsets
                    if (write1.MemoryAddress != write2.MemoryAddress)
                    {
                        Result = ComparisonResults.MemoryAccess_DifferentImageMemoryWriteOffset;
                        ComparisonErrorLine  = i;
                        ComparisonErrorItems = new Tuple <TraceEntry, TraceEntry>(e1, e2);
                        if (PrintResults)
                        {
                            PrintComparisonResult($"Entries #{i}: Memory write offsets in image differ: {write1.MemoryImageName}+{write1.MemoryAddress.ToString("X")} vs. {write2.MemoryImageName}+{write2.MemoryAddress.ToString("X")}", false,
                                                  $"Trace 1: {e1.ToString()}",
                                                  $"Trace 2: {e2.ToString()}");
                        }
                        return(false);
                    }

                    // OK
                    break;
                }

                // Possible leakages:
                // -> Different access offset (after subtracting allocation base addresses)
                case TraceEntryTypes.AllocMemoryRead:
                {
                    // Cast entries
                    AllocMemoryReadEntry read1 = (AllocMemoryReadEntry)e1;
                    AllocMemoryReadEntry read2 = (AllocMemoryReadEntry)e2;

                    // Find related allocation block
                    AllocationData allocCandidate = FindAllocationBlock(read1.MemoryAddress, read2.MemoryAddress);
                    if (allocCandidate == null)
                    {
                        // Stack access?
                        if (stackMemory.StartAddress1 <= read1.MemoryAddress && read1.MemoryAddress <= stackMemory.EndAddress1 &&
                            stackMemory.StartAddress2 <= read2.MemoryAddress && read2.MemoryAddress <= stackMemory.EndAddress2)
                        {
                            allocCandidate = stackMemory;
                        }
                        else
                        {
                            // TODO remove warning, better include the fs/gs segment bounds in the trace
                            if (PrintResults)
                            {
                                PrintComparisonResult($"Entries #{i}: Cannot find common accessed allocation block. Maybe there are segment registers involved?", true,
                                                      $"Trace 1: {read1.ToString()}",
                                                      $"Trace 2: {read2.ToString()}",
                                                      $"Stack: {stackMemory.ToString()}");
                            }
                            break;
                        }
                    }

                    // Calculate and compare relative access offsets
                    ulong offset1 = read1.MemoryAddress - allocCandidate.StartAddress1;
                    ulong offset2 = read2.MemoryAddress - allocCandidate.StartAddress2;
                    if (offset1 != offset2)
                    {
                        Result = ComparisonResults.MemoryAccess_DifferentAllocMemoryReadOffset;
                        ComparisonErrorLine  = i;
                        ComparisonErrorItems = new Tuple <TraceEntry, TraceEntry>(e1, e2);
                        if (PrintResults)
                        {
                            PrintComparisonResult($"Entries #{i}: Memory read offsets in common allocation block differ: +{offset1.ToString("X8")} vs. +{offset2.ToString("X8")}", false,
                                                  $"Trace 1: {e1.ToString()}",
                                                  $"Trace 2: {e2.ToString()}");
                        }
                        return(false);
                    }

                    // OK
                    break;
                }

                // Possible leakages:
                // -> Different access offset (after subtracting allocation/stack pointer base addresses)
                case TraceEntryTypes.AllocMemoryWrite:
                {
                    // Cast entries
                    AllocMemoryWriteEntry write1 = (AllocMemoryWriteEntry)e1;
                    AllocMemoryWriteEntry write2 = (AllocMemoryWriteEntry)e2;

                    // Find related allocation block
                    AllocationData allocCandidate = FindAllocationBlock(write1.MemoryAddress, write2.MemoryAddress);
                    if (allocCandidate == null)
                    {
                        // Stack access?
                        if (stackMemory.StartAddress1 <= write1.MemoryAddress && write1.MemoryAddress <= stackMemory.EndAddress1 &&
                            stackMemory.StartAddress2 <= write2.MemoryAddress && write2.MemoryAddress <= stackMemory.EndAddress2)
                        {
                            allocCandidate = stackMemory;
                        }
                        else
                        {
                            // TODO remove warning, better include the fs/gs segment bounds in the trace
                            if (PrintResults)
                            {
                                PrintComparisonResult($"Entries #{i}: Cannot find common accessed allocation block. Maybe there are segment registers involved?", true,
                                                      $"Trace 1: {write1.ToString()}",
                                                      $"Trace 2: {write2.ToString()}",
                                                      $"Stack: {stackMemory.ToString()}");
                            }
                            break;
                        }
                    }

                    // Calculate and compare relative access offsets
                    ulong offset1 = write1.MemoryAddress - allocCandidate.StartAddress1;
                    ulong offset2 = write2.MemoryAddress - allocCandidate.StartAddress2;
                    if (offset1 != offset2)
                    {
                        Result = ComparisonResults.MemoryAccess_DifferentAllocMemoryWriteOffset;
                        ComparisonErrorLine  = i;
                        ComparisonErrorItems = new Tuple <TraceEntry, TraceEntry>(e1, e2);
                        if (PrintResults)
                        {
                            PrintComparisonResult($"Entries #{i}: Memory write offsets in common allocation block differ: +{offset1.ToString("X8")} vs. +{offset2.ToString("X8")}", false,
                                                  $"Trace 1: {e1.ToString()}",
                                                  $"Trace 2: {e2.ToString()}");
                        }
                        return(false);
                    }

                    // OK
                    break;
                }
                }
            }

            // No differences found
            Result = ComparisonResults.Match;
            return(true);
        }