diff --git a/integration_tests/test_dumper.py b/integration_tests/test_dumper.py index 9e68ecf..b8bc1be 100644 --- a/integration_tests/test_dumper.py +++ b/integration_tests/test_dumper.py @@ -429,7 +429,21 @@ def _check_header(heap: Heap, dump_str_repr: bool) -> None: assert (datetime.now(timezone.utc) - x).total_seconds() < 5 * 60 assert heap.header.flags.with_str_repr is dump_str_repr - for type_name in ["dict", "set", "list", "tuple"]: + for type_name in [ + "dict", + "set", + "list", + "tuple", + "str", + "bytes", + "bytearray", + "int", + "bool", + "float", + "object", + "type", + "NoneType", + ]: dict_type_addr = heap.header.well_known_types[type_name] assert heap.types[dict_type_addr] == type_name if dump_str_repr: diff --git a/pyheap-ui/src/pyheap_ui/__main__.py b/pyheap-ui/src/pyheap_ui/__main__.py index 0dc825f..e1b52c9 100644 --- a/pyheap-ui/src/pyheap_ui/__main__.py +++ b/pyheap-ui/src/pyheap_ui/__main__.py @@ -15,16 +15,17 @@ # import argparse import dataclasses +import functools import logging import mmap import os import time import math -from typing import Optional, Any +from typing import Optional, Any, Dict from flask import Flask, render_template, abort, request from flask.json.provider import DefaultJSONProvider -from .heap_types import Heap, JsonObject +from .heap_types import Heap, JsonObject, Address from .heap_reader import HeapReader from .heap import ( provide_retained_heap_with_caching, @@ -109,20 +110,25 @@ def objects(address: int) -> str: obj = heap.objects[address] - well_known_type = next( - (k for k, v in heap.header.well_known_types.items() if v == obj.type), None - ) - + is_type_type = obj.type == heap.header.well_known_types.get("type") + type_instances = None + if is_type_type: + type_instances = [ + addr for addr, obj in heap.objects.items() if obj.type == address + ] return render_template( "objects.html", tab_object_active=True, address=address, obj=obj, + type_address=obj.type, type=heap.types[obj.type], objects=heap.objects, types=heap.types, retained_heap=retained_heap, - well_known_type=well_known_type, + well_known_container_type=well_known_container_types().get(obj.type), + is_type_type=is_type_type, + type_instances=type_instances, ) @@ -159,6 +165,16 @@ def big_number(size: int) -> str: return " ".join(chunks) +@functools.lru_cache +def well_known_container_types() -> Dict[Address, str]: + return { + heap.header.well_known_types["dict"]: "dict", + heap.header.well_known_types["list"]: "list", + heap.header.well_known_types["set"]: "set", + heap.header.well_known_types["tuple"]: "tuple", + } + + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Heap viewer UI.", allow_abbrev=False) parser.add_argument("--file", "-f", type=str, required=True, help="heap file name") diff --git a/pyheap-ui/src/pyheap_ui/heap_reader.py b/pyheap-ui/src/pyheap_ui/heap_reader.py index 4ee61fa..2d8c3b2 100644 --- a/pyheap-ui/src/pyheap_ui/heap_reader.py +++ b/pyheap-ui/src/pyheap_ui/heap_reader.py @@ -189,7 +189,12 @@ def _read_heap_object(self, address: Address) -> HeapObject: type_ = self._read(Address) size_ = self._read(UnsignedInt) - is_well_known_container_type = type_ in self._header.well_known_types.values() + is_well_known_container_type = type_ in { + self._header.well_known_types["dict"], + self._header.well_known_types["list"], + self._header.well_known_types["set"], + self._header.well_known_types["tuple"], + } content: ObjectContent = None extra_referents: Set[Address] = set() diff --git a/pyheap-ui/src/pyheap_ui/templates/objects.html b/pyheap-ui/src/pyheap_ui/templates/objects.html index 027dc3e..295411c 100644 --- a/pyheap-ui/src/pyheap_ui/templates/objects.html +++ b/pyheap-ui/src/pyheap_ui/templates/objects.html @@ -66,7 +66,7 @@

Address: {{ address }}

-

Type: {{ type }}

+

Type: {{ render_object_link(type_address) }} {{ type }}

Size: {{ obj.size | big_number | safe }} B

Retained heap: {{ retained_heap.get_for_object(address) | big_number | safe }} B

String representation: @@ -137,11 +137,11 @@

- {% if well_known_type is not none %} + {% if well_known_container_type is not none %}

@@ -150,12 +150,12 @@

aria-labelledby="panelElements">
    - {% if well_known_type == "dict" %} + {% if well_known_container_type == "dict" %} {% for key, value in obj.content.items() %}
  • {{ render_element(key) }}  :  {{ render_element(value) }}
  • {% endfor %} {% endif %} - {% if well_known_type in ["list", "set", "tuple"] %} + {% if well_known_container_type in ["list", "set", "tuple"] %} {% for el in obj.content %}
  • {{ render_element(el) }}
  • {% endfor %} @@ -166,6 +166,28 @@

{% endif %} + {% if is_type_type %} +
+

+ +

+
+
+
    + {% for addr in type_instances %} +
  • {{ render_element(addr) }} + {% endfor %} +
+
+
+
+ {% endif %} +