Skip to content

Feature: new renderer support Mermaid πŸ§œβ€β™€οΈ (for tree relationship).#737

Merged
ikelos merged 14 commits into
volatilityfoundation:developfrom
digitalisx:feature/mermaid
May 26, 2026
Merged

Feature: new renderer support Mermaid πŸ§œβ€β™€οΈ (for tree relationship).#737
ikelos merged 14 commits into
volatilityfoundation:developfrom
digitalisx:feature/mermaid

Conversation

@digitalisx

@digitalisx digitalisx commented May 14, 2022

Copy link
Copy Markdown
Contributor

Description

Hello, everyone in the community! πŸ™‚
Through dot-based rendering in Volatility, we were able to conduct an effective analysis by visualizing the relationship between processes and the composition of VAD in the form of trees.
Volatility 3 now helps us visualize tree-like information through Depth, but there are limitations.

I thought a lot about whether to migrate dot first.
Dot is still a good visualization tool and helps many developers and analysts.
However, I would like to propose a new instrument called mermaid.
Mermaid, (Repository) is a good tool for dynamically rendering charts or graphs based on markdowns.
When we sometimes had to share dots with the outside world using dots, we needed to share files, take screenshots, or have a separate viewer.
However, mermaid has the advantage of being able to share and write graphs neatly in SVG form easily in markdown or web. (Github also officially supports this and is included as an example in this PR.)

Of course, if you want to use Mermaid more precisely, you need to change the rendering structure. (Unlike before, we don't seem to have the means to configure rendering on each plugin.) But I think it can get better gradually.

I submitted a PR for the basic Mermaid rendering code. This is sufficient to indicate tree relationships such as PID and PPID, and feedback from other analysts was also available.

  • Create Mermaid renderer skeleton code
  • Constructing Trees (Node, Edge Data)
  • Refactoring for relation decision
  • Test for plugins
  • Code Review

Command

> python3 vol.py -f 32.vmem -r mermaid windows.pstree

Result

Terminal Text

> python3 vol.py -f case.vmem -r mermaid windows.pstree
Volatility 3 Framework 2.2.0
Formatting...0.00               PDB scanning finished                        
graph TD
        0 --> 4[PID:4<br>PPID:0<br>ImageFileName:System<br>OffsetV:0x86359280<br>Threads:106<br>Handles:-<br>SessionId:N/A<br>Wow64:False<br>CreateTime:2022-04-26 04:57:11.000000 <br>ExitTime:N/A<br>]
        4 --> 304[PID:304<br>PPID:4<br>ImageFileName:smss.exe<br>OffsetV:0x8c82f040<br>Threads:4<br>Handles:-<br>SessionId:N/A<br>Wow64:False<br>CreateTime:2022-04-26 04:57:11.000000 <br>ExitTime:N/A<br>]
        4 --> 92[PID:92<br>PPID:4<br>ImageFileName:Registry<br>OffsetV:0x87e28640<br>Threads:4<br>Handles:-<br>SessionId:N/A<br>Wow64:False<br>CreateTime:2022-04-26 04:57:06.000000 <br>ExitTime:N/A<br>]

Embedded

graph TD
        0 --> 4[PID:4<br>PPID:0<br>ImageFileName:System<br>OffsetV:0x86359280<br>Threads:106<br>Handles:-<br>SessionId:N/A<br>Wow64:False<br>CreateTime:2022-04-26 04:57:11.000000 <br>ExitTime:N/A<br>]
        4 --> 304[PID:304<br>PPID:4<br>ImageFileName:smss.exe<br>OffsetV:0x8c82f040<br>Threads:4<br>Handles:-<br>SessionId:N/A<br>Wow64:False<br>CreateTime:2022-04-26 04:57:11.000000 <br>ExitTime:N/A<br>]
        4 --> 92[PID:92<br>PPID:4<br>ImageFileName:Registry<br>OffsetV:0x87e28640<br>Threads:4<br>Handles:-<br>SessionId:N/A<br>Wow64:False<br>CreateTime:2022-04-26 04:57:06.000000 <br>ExitTime:N/A<br>]
        4 --> 1564[PID:1564<br>PPID:4<br>ImageFileName:MemCompression<br>OffsetV:0x863b35c0<br>Threads:18<br>Handles:-<br>SessionId:N/A<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        384 --> 404[PID:404<br>PPID:384<br>ImageFileName:csrss.exe<br>OffsetV:0x9244c0c0<br>Threads:14<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        384 --> 480[PID:480<br>PPID:384<br>ImageFileName:wininit.exe<br>OffsetV:0x92530040<br>Threads:8<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        480 --> 616[PID:616<br>PPID:480<br>ImageFileName:services.exe<br>OffsetV:0x8cd14040<br>Threads:12<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        616 --> 768[PID:768<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x92596680<br>Threads:25<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        768 --> 2976[PID:2976<br>PPID:768<br>ImageFileName:SearchApp.exe<br>OffsetV:0x9d18d600<br>Threads:29<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:18.000000 <br>ExitTime:N/A<br>]
        768 --> 4544[PID:4544<br>PPID:768<br>ImageFileName:dllhost.exe<br>OffsetV:0x863d5040<br>Threads:8<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:20.000000 <br>ExitTime:N/A<br>]
        768 --> 4072[PID:4072<br>PPID:768<br>ImageFileName:StartMenuExper<br>OffsetV:0x977b6040<br>Threads:35<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:18.000000 <br>ExitTime:N/A<br>]
        768 --> 4200[PID:4200<br>PPID:768<br>ImageFileName:backgroundTask<br>OffsetV:0xa1c9e640<br>Threads:7<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:19.000000 <br>ExitTime:N/A<br>]
        768 --> 4780[PID:4780<br>PPID:768<br>ImageFileName:mobsync.exe<br>OffsetV:0xa833a800<br>Threads:8<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:22.000000 <br>ExitTime:N/A<br>]
        768 --> 5148[PID:5148<br>PPID:768<br>ImageFileName:RuntimeBroker.<br>OffsetV:0x965657c0<br>Threads:10<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:24.000000 <br>ExitTime:N/A<br>]
        768 --> 3792[PID:3792<br>PPID:768<br>ImageFileName:SppExtComObj.E<br>OffsetV:0xa04db040<br>Threads:8<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:17.000000 <br>ExitTime:N/A<br>]
        768 --> 4464[PID:4464<br>PPID:768<br>ImageFileName:dllhost.exe<br>OffsetV:0xa1ce0800<br>Threads:8<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:19.000000 <br>ExitTime:N/A<br>]
        768 --> 1044[PID:1044<br>PPID:768<br>ImageFileName:WmiPrvSE.exe<br>OffsetV:0x9d01f600<br>Threads:11<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:18.000000 <br>ExitTime:N/A<br>]
        768 --> 2132[PID:2132<br>PPID:768<br>ImageFileName:RuntimeBroker.<br>OffsetV:0x9d082680<br>Threads:21<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:18.000000 <br>ExitTime:N/A<br>]
        768 --> 2460[PID:2460<br>PPID:768<br>ImageFileName:RuntimeBroker.<br>OffsetV:0x9d1df040<br>Threads:16<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:18.000000 <br>ExitTime:N/A<br>]
        616 --> 2444[PID:2444<br>PPID:616<br>ImageFileName:sppsvc.exe<br>OffsetV:0x9bdae7c0<br>Threads:9<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:13.000000 <br>ExitTime:N/A<br>]
        616 --> 2060[PID:2060<br>PPID:616<br>ImageFileName:SearchIndexer.<br>OffsetV:0x9d18a040<br>Threads:22<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:18.000000 <br>ExitTime:N/A<br>]
        2060 --> 4984[PID:4984<br>PPID:2060<br>ImageFileName:SearchFilterHo<br>OffsetV:0xa834c840<br>Threads:6<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:23.000000 <br>ExitTime:N/A<br>]
        2060 --> 4916[PID:4916<br>PPID:2060<br>ImageFileName:SearchProtocol<br>OffsetV:0xa1d5f040<br>Threads:9<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:23.000000 <br>ExitTime:N/A<br>]
        616 --> 1940[PID:1940<br>PPID:616<br>ImageFileName:spoolsv.exe<br>OffsetV:0x863fb880<br>Threads:13<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:13.000000 <br>ExitTime:N/A<br>]
        616 --> 3220[PID:3220<br>PPID:616<br>ImageFileName:NisSrv.exe<br>OffsetV:0xa043e840<br>Threads:9<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:15.000000 <br>ExitTime:N/A<br>]
        616 --> 1436[PID:1436<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x977a87c0<br>Threads:17<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:13.000000 <br>ExitTime:N/A<br>]
        616 --> 1820[PID:1820<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x863d5840<br>Threads:21<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        616 --> 1444[PID:1444<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x89ba1580<br>Threads:25<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        616 --> 2088[PID:2088<br>PPID:616<br>ImageFileName:MsMpEng.exe<br>OffsetV:0x8c24c040<br>Threads:29<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:13.000000 <br>ExitTime:N/A<br>]
        616 --> 1072[PID:1072<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x965d1680<br>Threads:65<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        1072 --> 336[PID:336<br>PPID:1072<br>ImageFileName:sihost.exe<br>OffsetV:0x97777880<br>Threads:21<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:13.000000 <br>ExitTime:N/A<br>]
        1072 --> 1580[PID:1580<br>PPID:1072<br>ImageFileName:taskhostw.exe<br>OffsetV:0x977b67c0<br>Threads:12<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:13.000000 <br>ExitTime:N/A<br>]
        616 --> 1200[PID:1200<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x9761d880<br>Threads:21<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        1200 --> 2360[PID:2360<br>PPID:1200<br>ImageFileName:ctfmon.exe<br>OffsetV:0x9bdae040<br>Threads:12<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:13.000000 <br>ExitTime:N/A<br>]
        616 --> 1968[PID:1968<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x863de880<br>Threads:19<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:13.000000 <br>ExitTime:N/A<br>]
        616 --> 3120[PID:3120<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x9bd05480<br>Threads:10<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:15.000000 <br>ExitTime:N/A<br>]
        616 --> 1472[PID:1472<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x8636f840<br>Threads:6<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        616 --> 1092[PID:1092<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x965d3440<br>Threads:21<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        616 --> 3656[PID:3656<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x9777b040<br>Threads:9<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:16.000000 <br>ExitTime:N/A<br>]
        616 --> 1108[PID:1108<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x965db040<br>Threads:22<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        616 --> 2780[PID:2780<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x9daf9640<br>Threads:39<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:14.000000 <br>ExitTime:N/A<br>]
        2780 --> 4708[PID:4708<br>PPID:2780<br>ImageFileName:rundll32.exe<br>OffsetV:0xa1d5c040<br>Threads:5<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:22.000000 <br>ExitTime:N/A<br>]
        616 --> 352[PID:352<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x97777080<br>Threads:17<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:13.000000 <br>ExitTime:N/A<br>]
        616 --> 3040[PID:3040<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x89bee040<br>Threads:13<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:15.000000 <br>ExitTime:N/A<br>]
        616 --> 868[PID:868<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x9652a680<br>Threads:18<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        616 --> 1764[PID:1764<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x863cd040<br>Threads:14<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        616 --> 1644[PID:1644<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x863c1040<br>Threads:14<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        616 --> 1264[PID:1264<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x976336c0<br>Threads:31<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        616 --> 1784[PID:1784<br>PPID:616<br>ImageFileName:svchost.exe<br>OffsetV:0x863d0840<br>Threads:7<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        480 --> 752[PID:752<br>PPID:480<br>ImageFileName:fontdrvhost.ex<br>OffsetV:0x925997c0<br>Threads:8<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        480 --> 652[PID:652<br>PPID:480<br>ImageFileName:lsass.exe<br>OffsetV:0x92474040<br>Threads:14<br>Handles:-<br>SessionId:0<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        472 --> 496[PID:496<br>PPID:472<br>ImageFileName:csrss.exe<br>OffsetV:0x92533080<br>Threads:15<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        472 --> 576[PID:576<br>PPID:472<br>ImageFileName:winlogon.exe<br>OffsetV:0x9256c680<br>Threads:8<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        576 --> 760[PID:760<br>PPID:576<br>ImageFileName:fontdrvhost.ex<br>OffsetV:0x92599040<br>Threads:8<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        576 --> 956[PID:956<br>PPID:576<br>ImageFileName:dwm.exe<br>OffsetV:0x96565040<br>Threads:22<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:12.000000 <br>ExitTime:N/A<br>]
        576 --> 2860[PID:2860<br>PPID:576<br>ImageFileName:userinit.exe<br>OffsetV:0x9db7b840<br>Threads:6<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:14.000000 <br>ExitTime:N/A<br>]
        2860 --> 2892[PID:2892<br>PPID:2860<br>ImageFileName:explorer.exe<br>OffsetV:0x9db92080<br>Threads:71<br>Handles:-<br>SessionId:1<br>Wow64:False<br>CreateTime:2022-04-26 04:57:15.000000 <br>ExitTime:N/A<br>]
Loading

Detail Screenshot

image

If you are interested in or have any comments on this PR, please feel free to leave a thread! πŸ™Œ

@digitalisx digitalisx changed the title Feature: new renderer support mermaid (for tree relationship). Feature: new renderer support Mermaid (for tree relationship). May 14, 2022
@ikelos

ikelos commented May 15, 2022

Copy link
Copy Markdown
Member

This looks like it'll be really awesome, but I'm afraid the renderer is hard coded with column names to produce the relationships (PID and PPID). Given that the parental relationship is already known by the TreeGrid (which rows are children), it should be fairly easy to remove the plugin specific parts of it?

I'd suggest giving each node a random identifier and then the first node that's one level higher before it, is it's parent (so each time there's a step down, pop the node on the end of a parent list, and use the end of the list as the parent, and each step up pop it off the list). That will make the renderer much more generic and work with any tree-based plugin, rather than just those that output PID and PPID. I'll be happy to re-review once it's been updated, but at the moment that immediately blocks it from inclusion in its current form.|

Another option would be to make this a very handy additional tool to run over existing output methods (such as JSON, JSONL or similar) if you'd prefer?

@digitalisx

Copy link
Copy Markdown
Contributor Author

This looks like it'll be really awesome, but I'm afraid the renderer is hard coded with column names to produce the relationships (PID and PPID). Given that the parental relationship is already known by the TreeGrid (which rows are children), it should be fairly easy to remove the plugin specific parts of it?

I'd suggest giving each node a random identifier and then the first node that's one level higher before it, is it's parent (so each time there's a step down, pop the node on the end of a parent list, and use the end of the list as the parent, and each step up pop it off the list). That will make the renderer much more generic and work with any tree-based plugin, rather than just those that output PID and PPID. I'll be happy to re-review once it's been updated, but at the moment that immediately blocks it from inclusion in its current form.|

Another option would be to make this a very handy additional tool to run over existing output methods (such as JSON, JSONL or similar) if you'd prefer?

@ikelos That's right, I think you gave me a good guide about the part I was worried about.

If so, we will change this PR to Draft PR because we still need more work and we are not ready for the review. If the prepared code is completed according to the feedback soon and it is enough to receive a review, I will request it again.

I hope it will be a good feature that many people will use.
Please give me a lot of advice! πŸ™‚

@digitalisx digitalisx marked this pull request as draft May 15, 2022 16:15
@digitalisx

digitalisx commented May 15, 2022

Copy link
Copy Markdown
Contributor Author

Another option would be to make this a very handy additional tool to run over existing output methods (such as JSON, JSONL or similar) if you'd prefer?

@ikelos Could you give me a little more detail about the this comment, I'll try it the way you first suggested it, but if it's a good suggestion to improve further, I'll be happy to accept it.

@ikelos

ikelos commented May 15, 2022

Copy link
Copy Markdown
Member

@ikelos Could you give me a little more detail about the this comment, I'll try it the way you first suggested it, but if it's a good suggestion to improve further, I'll be happy to accept it.

Sure, I just meant you could write it as a separate tool which takes in the existing output from volatility3 and then produces the mermaid output from that, so something like:

volatility3 -f inputfile.dmp -r json plugin.name | your-new-program -o mermaid.out

If you really wanted you could write a separate program (or a volshell snippet) which could do the same thing. I think that make it a renderer makes more sense, but if it was tricky to make it generic then this would be another option. 5:)

@digitalisx

digitalisx commented May 17, 2022

Copy link
Copy Markdown
Contributor Author

@ikelos Could you give me a little more detail about the this comment, I'll try it the way you first suggested it, but if it's a good suggestion to improve further, I'll be happy to accept it.

Sure, I just meant you could write it as a separate tool which takes in the existing output from volatility3 and then produces the mermaid output from that, so something like:

volatility3 -f inputfile.dmp -r json plugin.name | your-new-program -o mermaid.out

If you really wanted you could write a separate program (or a volshell snippet) which could do the same thing. I think that make it a renderer makes more sense, but if it was tricky to make it generic then this would be another option. 5:)

@ikelos Ah I understand, but I also think the best way is to implement it as a renderer. Thank you for the detailed explanation. πŸ™‚

@digitalisx digitalisx changed the title Feature: new renderer support Mermaid (for tree relationship). Feature: new renderer support Mermaid πŸ§œβ€β™€οΈ (for tree relationship). May 23, 2022
Comment thread volatility3/cli/text_renderer.py Fixed
Address review feedback that the renderer was tightly coupled to plugins
exposing PID/PPID columns: the previous implementation looked up "PID"
and "PPID" by name to build parent->child edges and raised a generic
exception otherwise, which prevented any non-pstree tree plugin from
being rendered as Mermaid.

The relationship is already encoded in the TreeGrid -- every TreeNode
carries its path_depth -- so the new render() walks the rows in
traversal order and tracks ancestry with a parent stack:

  * descending one or more levels pushes the previously-emitted node
    once per level (so a level skip still produces sane pops);
  * ascending pops the corresponding number of levels;
  * the stack top is always the parent of the next emitted node, or
    empty for a root-level node.

Each node is given a stable per-render identifier (n1, n2, ...) instead
of being keyed by a column value such as PID, since PIDs are not unique
across a TreeGrid and may contain characters that are unsafe in Mermaid
node IDs.

A small label-escaping helper replaces the previous ad-hoc string
replacement of parentheses, and embedded newlines in cell renderings
are folded to <br> so each row stays a single Mermaid node.

The unused tree_indent_column placeholder (flagged by code scanning)
is dropped as part of the rewrite.
Bring the new MermaidRenderer in line with the repository's ruff
formatting rules (introduced in the develop merge that this branch
just absorbed): double-quoted strings, trailing comma after the last
mapping entry, four-space hanging indent for the visitor signature,
and an additional blank line between top-level classes.
@digitalisx digitalisx marked this pull request as ready for review May 26, 2026 05:06
@digitalisx

Copy link
Copy Markdown
Contributor Author

Hi @ikelos β€” coming back to this one after rather a long term.

Could you please review this PR when you have time? The renderer is now plugin-agnostic: instead of looking up PID / PPID columns by name, it uses each TreeNode's path_depth and
a parent stack to derive the parent of every emitted node β€” pretty much exactly the algorithm you suggested.

@ikelos ikelos left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking the time to rework this, it looks much more flexible and applicable to all outputs! 5:D Just one minor point, and it's not a show stopper, but it feels like next_id could be a generator (specifically the itertools.count generator). Otherwise this looks good!

Comment thread volatility3/cli/text_renderer.py Outdated
Per review feedback, the small next_id() closure that combined a
'nonlocal node_counter' assignment with an f-string formatter is more
naturally expressed as itertools.count. The counter generator yields
the integer sequence starting at 1 and the call site formats it as
'n<index>' on the spot, so the behaviour is unchanged: per-render,
strictly-increasing, plugin-agnostic Mermaid node identifiers.
@digitalisx digitalisx requested a review from ikelos May 26, 2026 07:35
@ikelos

ikelos commented May 26, 2026

Copy link
Copy Markdown
Member

Looks good, thanks! I'll merge this once the tests have completed. 5:)

@ikelos ikelos merged commit 634774f into volatilityfoundation:develop May 26, 2026
13 checks passed
@digitalisx digitalisx deleted the feature/mermaid branch May 26, 2026 09:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants