Commit 4a26a665 authored by EmaJasekova's avatar EmaJasekova
Browse files

Added "ShowAll" button - display complete memory of selected node

parent 6b499d04
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -25,6 +25,18 @@
            <artifactId>flatlaf</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.9.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.9.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
+113 −13
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;

/**
 * Panel that displays memory of selected execution state
@@ -44,8 +45,7 @@ public class MemoryViewer extends JPanel implements ListSelectionListener {
                if (showAll) {
                    showAllButton.setText("Hide");
                    displayCompleteMemory(currentState);
                }
                else {
                } else {
                    showAllButton.setText("Show All");
                    displayShortMemory(currentState);
                }
@@ -124,8 +124,9 @@ public class MemoryViewer extends JPanel implements ListSelectionListener {
            return;
        }

        objectsList.setSelectedIndex(0);
        updatePlanes();
//        objectsList.setSelectedIndex(0);
//        updatePlanes();
//        displayObjectInfo();
    }

    private void appendKeyValue(StyledDocument doc, String key, String value) {
@@ -148,6 +149,8 @@ public class MemoryViewer extends JPanel implements ListSelectionListener {
        objectInfoPanel.removeAll();

        if (objectsList.getSelectedIndex() < 0) {
            revalidate();
            repaint();
            return;
        }

@@ -194,13 +197,14 @@ public class MemoryViewer extends JPanel implements ListSelectionListener {
        appendKeyValue(doc, "Symbolic Address", currentObjectState.symAddress());
        appendKeyValue(doc, "Copy-On-Write Owner", String.valueOf(currentObjectState.copyOnWriteOwner()));
        appendKeyValue(doc, "Read-Only", String.valueOf(currentObjectState.readOnly()));

        if (currentObjectState.segmentPlane() != null) {
            appendPlaneInfo(doc, "Segment Plane", currentObjectState.segmentPlane());
        }
        if (currentObjectState.offsetPlane() != null) {
            appendPlaneInfo(doc, "Offset Plane", currentObjectState.offsetPlane());
        }
        objectInfoPanel.revalidate();
        objectInfoPanel.repaint();
    }

    private void appendPlaneInfo(StyledDocument doc, String planeType, NodeMemory.Plane plane) {
@@ -222,18 +226,112 @@ public class MemoryViewer extends JPanel implements ListSelectionListener {
        appendKeyValue(doc, "Initial Value", String.valueOf(plane.initialValue()));
    }

    private void getCompleteMemory(Node node, NodeMemory.Memory memory) {
        if (node == null) {
            return;
    private static NodeMemory.ObjectState mergeObjectState(NodeMemory.ObjectState a, NodeMemory.ObjectState b) {
        // copy all fields from a
        NodeMemory.Plane mergedSegmentPlane = mergePlane(a.segmentPlane(), b.segmentPlane());
        NodeMemory.Plane mergedOffsetPlane = mergePlane(a.offsetPlane(), b.offsetPlane());

        return new NodeMemory.ObjectState(
                a.objID(), a.type(), a.segment(), a.name(), a.size(), a.isLocal(), a.isGlobal(),
                a.isFixed(), a.isUserSpec(), a.isLazy(), a.symAddress(), a.copyOnWriteOwner(),
                a.readOnly(), mergedSegmentPlane, mergedOffsetPlane);
    }

    private static NodeMemory.Plane mergePlane(NodeMemory.Plane a, NodeMemory.Plane b) {
        if (a == null && b == null) {
            return null;
        }
        if (a == null) {
            return b;
        }
        if (b == null) {
            return a;
        }
        return new NodeMemory.Plane(
                a.type(), a.memoryObjectID(), a.rootObject(), a.sizeBound(), a.initialized(),
                a.symbolic(), a.initialValue(),
                mergeDiff(a.concreteStore(), b.concreteStore()),
                mergeDiff(a.concreteMask(), b.concreteMask()),
                mergeDiff(a.knownSymbolics(), b.knownSymbolics()),
                mergeUpdates(a.updates(), b.updates()));
    }

    private static NodeMemory.Updates mergeUpdates(NodeMemory.Updates a, NodeMemory.Updates b) {
        NodeMemory.Updates mergedUpdates = new NodeMemory.Updates();

        if (a != null) {
            mergedUpdates.putAll(a);
        }

        if (b != null) {
            mergedUpdates.putAll(b);
        }
        return mergedUpdates;
    }

    public static NodeMemory.Diff mergeDiff(NodeMemory.Diff a, NodeMemory.Diff b) {
        NodeMemory.ByteMap mergedByteMap = new NodeMemory.ByteMap();

        // Copy all entries from 'a' into 'mergedByteMap'
        a.additions().forEach((key, indices) -> {
            mergedByteMap.put(key, new ArrayList<>(indices));
        });

        // Delete all entries from 'b's deletions
        b.deletions().forEach((key, indices) -> {
            mergedByteMap.get(key).removeAll(indices);
        });

        // Remove all empty entries from 'mergedByteMap'
        mergedByteMap.entrySet().removeIf(entry -> entry.getValue().isEmpty());

        // Iterate over each addition in 'b' and merge it into 'mergedByteMap'
        b.additions().forEach((key, indices) -> {
            // If the key exists in 'mergedByteMap', merge the indices by adding all elements of 'indices' to 'mergedByteMap'
            mergedByteMap.merge(key, indices, (v1, v2) -> {
                v1.addAll(v2);
                return v1;
            });
        });

        getCompleteMemory(node.getParent(), memory);
        return new NodeMemory.Diff(
                mergedByteMap,
                // The complete state does not contain deletions
                new NodeMemory.ByteMap());
    }

    private void displayCompleteMemory(Node node) {
        NodeMemory.Memory m = new NodeMemory.Memory(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
        getCompleteMemory(node, m);
        displayTables(m);
        HashMap<Integer, NodeMemory.ObjectState> complete_memory = new HashMap<>();
        ArrayList<Node> nodes = new ArrayList<>();

        nodes.add(node);
        while (node.getParent() != null) {
            node = node.getParent();
            nodes.add(node);
        }
        // Now we have the nodes in order from leaf to root, traverse them in reverse order
        for (int i = nodes.size() - 1; i >= 0; i--) {
            NodeMemory.Memory node_memory = nodes.get(i).getMemory().getMemory();

            // Add newly added objects
            for (NodeMemory.ObjectState addition : node_memory.additions()) {
                complete_memory.put(addition.objID(), addition);
            }

            // Apply changes to changed objects
            for (NodeMemory.ObjectState change : node_memory.changes()) {
                NodeMemory.ObjectState oldObjectState = complete_memory.get(change.objID());
                complete_memory.put(change.objID(), mergeObjectState(oldObjectState, change));
            }

            // Remove deleted objects
            for (NodeMemory.Deletion deletion : node_memory.deletions()) {
                complete_memory.remove(deletion.objID());
            }
        }

        NodeMemory.Memory memory = new NodeMemory.Memory(new ArrayList<>(complete_memory.values()), new ArrayList<>(), new ArrayList<>());
        displayTables(memory);
        displayObjectInfo();
    }

@@ -243,6 +341,8 @@ public class MemoryViewer extends JPanel implements ListSelectionListener {
    }

    public void displayMemory(Node node) {
        showAllButton.setText("Show All");
        showAll = false;
        currentState = node;
        currentMemory = node.getMemory().getMemory();

@@ -310,8 +410,8 @@ public class MemoryViewer extends JPanel implements ListSelectionListener {
        if (isDeletion) {
            handleDeletionClick(selectedID);
        } else {
            displayObjectInfo();
            updatePlanes();
            displayObjectInfo();
        }
    }

+16 −3
Original line number Diff line number Diff line
@@ -5,7 +5,6 @@ import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

/**
 * Parses and holds information about execution state of the process tree
@@ -30,6 +29,9 @@ public class NodeMemory {
    public static class ByteMap extends HashMap<String, ArrayList<Integer>> {
    }

    public record AllocSite(String scope, String name, String code) {
    }

    public record Plane(PlaneType type, int memoryObjectID, String rootObject, int sizeBound,
                        boolean initialized, boolean symbolic, int initialValue,
                        Diff concreteStore, Diff concreteMask, Diff knownSymbolics,
@@ -50,7 +52,7 @@ public class NodeMemory {
                         ArrayList<Deletion> deletions) {
    }

    public class Updates extends HashMap<String, String> {
    public static class Updates extends HashMap<String, String> {
    }

    public record Deletion(int objID, OperationType type) {
@@ -101,6 +103,16 @@ public class NodeMemory {
        for (int i = 0; i < objectStatesJSON.length(); i++) {
            JSONObject objectStateJSON = objectStatesJSON.getJSONObject(i);

//            AllocSite allocSite = null;
//            if (objectStateJSON.has("allocSite")) {
//                JSONObject allocSiteJSON = objectStateJSON.getJSONObject("allocSite");
//                allocSite = new AllocSite(
//                        allocSiteJSON.getString("scope"),
//                        allocSiteJSON.getString("name"),
//                        allocSiteJSON.getString("code")
//                );
//            }

            ObjectState objectState = new ObjectState(
                    objectStateJSON.getInt("objID"),
                    type,
@@ -115,6 +127,7 @@ public class NodeMemory {
                    objectStateJSON.getString("symAddress"),
                    objectStateJSON.getInt("copyOnWriteOwner"),
                    objectStateJSON.getInt("readOnly") == 1,
//                    allocSite,
                    parsePlane(objectStateJSON, Plane.PlaneType.SEGMENT),
                    parsePlane(objectStateJSON, Plane.PlaneType.OFFSET)
            );
@@ -187,7 +200,7 @@ public class NodeMemory {
                JSONObject updateJSON = updatesJSON.getJSONObject(i);
                String key = updateJSON.keys().next();
                String value = updateJSON.getString(key);
                updates.put(key, value);
                updates.put(value, key);
            }
        }

+7 −6
Original line number Diff line number Diff line
@@ -36,14 +36,15 @@ public class PlanePanel extends JPanel {

        updatePanel = new JPanel(new BorderLayout());

        // Create a JSplitPane to display the concrete and symbolic panels side by side
        JSplitPane concreteSymbolicSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, concretePanel, symbolicPanel);
        concreteSymbolicSplitPane.setResizeWeight(0.5); // Divide space equally between panels
        JSplitPane bytesSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, concretePanel, symbolicPanel);
        bytesSplitPane.setResizeWeight(0.5);

        // Create a vertical JSplitPane to display the control panel and the combined concrete/symbolic panel
        JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, controlPanel, concreteSymbolicSplitPane);
        JTabbedPane tabbedPane = new JTabbedPane();
        tabbedPane.addTab("Bytes", bytesSplitPane);
        tabbedPane.addTab("Update", updatePanel);

        this.add(splitPane, BorderLayout.CENTER);
        this.add(controlPanel, BorderLayout.NORTH);
        this.add(tabbedPane, BorderLayout.CENTER);
    }

    private void clearTable(JPanel panel) {
+118 −0
Original line number Diff line number Diff line
import jetklee.NodeMemory;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static jetklee.MemoryViewer.mergeDiff;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class TestMergeDiff {

    // Method to convert Map<String, List<Integer>> to ByteMap
    private static NodeMemory.ByteMap convertToByteMap(Map<String, List<Integer>> map) {
        NodeMemory.ByteMap byteMap = new NodeMemory.ByteMap();
        for (Map.Entry<String, List<Integer>> entry : map.entrySet()) {
            byteMap.put(entry.getKey(), new ArrayList<>(entry.getValue()));
        }
        return byteMap;
    }

    @Test
    public void testMergeDiffSimple() {
        // Arrange: Set up diff1, diff2, and the expected result
        NodeMemory.Diff diff1 = new NodeMemory.Diff(
                // additions
                convertToByteMap(Map.of(
                        "0", List.of(0, 1, 2, 3)
                )),
                // deletions
                new NodeMemory.ByteMap()
        );

        NodeMemory.Diff diff2 = new NodeMemory.Diff(
                // additions
                convertToByteMap(Map.of(
                        "1", List.of(1, 4)
                )),
                // deletions
                convertToByteMap(Map.of(
                        "0", List.of(1, 2)
                ))
        );

        NodeMemory.Diff expected = new NodeMemory.Diff(
                // additions
                convertToByteMap(Map.of(
                        "0", List.of(0, 3),
                        "1", List.of(1, 4)
                )),
                // deletions
                new NodeMemory.ByteMap()
        );


        // Act: Call the mergeDiff function
        NodeMemory.Diff result = mergeDiff(diff1, diff2);

        // check if the original diff1 and diff2 are not modified
        NodeMemory.Diff diff1Expected = new NodeMemory.Diff(
                convertToByteMap(Map.of(
                        "0", List.of(0, 1, 2, 3)
                )),
                new NodeMemory.ByteMap()
        );
        NodeMemory.Diff diff2Expected = new NodeMemory.Diff(
                convertToByteMap(Map.of(
                        "1", List.of(1, 4)
                )),
                convertToByteMap(Map.of(
                        "0", List.of(1, 2)
                ))
        );

        assertEquals(diff1Expected, diff1, "The original diff1 is modified");
        assertEquals(diff2Expected, diff2, "The original diff2 is modified");

        // Assert: Check if the result matches the expected output
        assertEquals(expected, result, "The merged diff is incorrect");
    }

    @Test
    public void testMergeDiffDuplicateKey() {
        // Arrange: Set up diff1, diff2, and the expected result
        NodeMemory.Diff diff1 = new NodeMemory.Diff(
                // additions
                convertToByteMap(Map.of(
                        "0", List.of(0, 1, 2, 3)
                )),
                // deletions
                new NodeMemory.ByteMap()
        );

        NodeMemory.Diff diff2 = new NodeMemory.Diff(
                // additions
                convertToByteMap(Map.of(
                        "222", List.of(0)
                )),
                // deletions
                convertToByteMap(Map.of(
                        "0", List.of(0)
                ))
        );

        NodeMemory.Diff expected = new NodeMemory.Diff(
                // additions
                convertToByteMap(Map.of(
                        "0", List.of(1, 2, 3),
                        "222", List.of(0)
                )),
                new NodeMemory.ByteMap()
        );

        // Act: Call the mergeDiff function
        NodeMemory.Diff result = mergeDiff(diff1, diff2);

        // Assert: Check if the result matches the expected output
        assertEquals(expected, result, "The merged diff is incorrect");
    }
}