[PATCH v2 4/5] ShellPkg: add dot file generator functions


Joey Gouly
 

From: Marc Moisson-Franckhauser <marc.moisson-franckhauser@arm.com>

Bugzilla: 3378 (https://bugzilla.tianocore.org/show_bug.cgi?id=3378)

These can be used to generate dot files, that can be used to visualise
graphs in tables, such as PPTT.

Signed-off-by: Joey Gouly <joey.gouly@arm.com>
---
ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.inf | 4 +-
ShellPkg/Library/UefiShellAcpiViewCommandLib/DotGenerator.h | 101 +++++++
ShellPkg/Library/UefiShellAcpiViewCommandLib/DotGenerator.c | 283 ++++++++++++++++++++
3 files changed, 387 insertions(+), 1 deletion(-)

diff --git a/ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.inf b/ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.inf
index 63fc5a1281a894841dac704484c3d4f9481edb46..ffe4979b3ac5d0120bcf678cf7823afac6674e4f 100644
--- a/ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.inf
+++ b/ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.inf
@@ -1,7 +1,7 @@
## @file
# Provides Shell 'acpiview' command functions
#
-# Copyright (c) 2016 - 2020, Arm Limited. All rights reserved.<BR>
+# Copyright (c) 2016 - 2021, Arm Limited. All rights reserved.<BR>
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
@@ -27,6 +27,8 @@ [Sources.common]
AcpiView.h
AcpiViewConfig.c
AcpiViewConfig.h
+ DotGenerator.c
+ DotGenerator.h
Parsers/Aest/AestParser.c
Parsers/Bgrt/BgrtParser.c
Parsers/Dbg2/Dbg2Parser.c
diff --git a/ShellPkg/Library/UefiShellAcpiViewCommandLib/DotGenerator.h b/ShellPkg/Library/UefiShellAcpiViewCommandLib/DotGenerator.h
new file mode 100644
index 0000000000000000000000000000000000000000..8e196eee75417c0a422023c00b043441076ac599
--- /dev/null
+++ b/ShellPkg/Library/UefiShellAcpiViewCommandLib/DotGenerator.h
@@ -0,0 +1,101 @@
+/** @file
+ Header file for Dot File Generation
+
+ Copyright (c) 2021, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef DOT_GENERATOR_H_
+#define DOT_GENERATOR_H_
+
+#include <Protocol/Shell.h>
+
+#define DOT_COLOR_MASK 0b111
+// Flags for color of arrow or node.
+#define DOT_COLOR_BLACK 0b000 // default
+#define DOT_COLOR_GRAY 0b001
+#define DOT_COLOR_BLUE 0b010
+#define DOT_COLOR_YELLOW 0b011
+#define DOT_COLOR_RED 0b100
+
+#define DOT_ARROW_TYPE_MASK 0b1000
+// Flags for style of arrow.
+#define DOT_ARROW_FULL 0b0000 // default
+#define DOT_ARROW_DOTTED 0b1000
+
+// Flag for reversing how the nodes will be ranked and displayed.
+#define DOT_ARROW_RANK_REVERSE 0b10000
+
+#define DOT_BOX_TYPE_MASK 0b1100000
+// Flag for shape of box
+#define DOT_BOX_SQUARE 0b0000000 // default
+#define DOT_BOX_DIAMOND 0b0100000
+
+// Flag for adding the node's ID to the end of the label.
+#define DOT_BOX_ADD_ID_TO_LABEL 0b10000000
+
+// Valid flags for DotAddNode.
+#define DOT_BOX_FLAGS_MASK (DOT_COLOR_MASK |\
+ DOT_BOX_TYPE_MASK |\
+ DOT_BOX_ADD_ID_TO_LABEL)
+// Valid flags for DotAddLink.
+#define DOT_ARROW_FLAGS_MASK (DOT_COLOR_MASK |\
+ DOT_ARROW_TYPE_MASK |\
+ DOT_ARROW_RANK_REVERSE)
+
+
+/**
+ Opens a new dot file and writes a dot directional graph.
+
+ @param [in] FileName Null terminated unicode string.
+**/
+SHELL_FILE_HANDLE
+DotOpenNewFile (
+ IN CHAR16* FileName
+ );
+
+/**
+ Writes a dot graph footer and closes the dot file.
+
+ @param [in] DotFileHandle The handle of the dot file.
+**/
+VOID
+DotCloseFile (
+ SHELL_FILE_HANDLE DotFileHandle
+ );
+
+/**
+ Writes a line in the previously opened dot file describing a
+ new node.
+
+ @param [in] DotFileHandle The handle of the dot file.
+ @param [in] Id A unique identifier for the node.
+ @param [in] Flags Flags describing the node's characteristics.
+ @param [in] Label Label to be shown on the graph node.
+**/
+VOID
+DotAddNode (
+ SHELL_FILE_HANDLE DotFileHandle,
+ IN UINT32 Id,
+ IN UINT16 Flags,
+ IN CONST CHAR16* Label
+ );
+
+/**
+ Writes a line in the previously opened dot file describing a
+ new link between two nodes.
+
+ @param [in] DotFileHandle The handle of the dot file.
+ @param [in] IdSource An identifier for the source node of the link.
+ @param [in] IdTarget An identifier for the target node of the link.
+ @param [in] Flags Flags describing the node's characteristics.
+**/
+VOID
+DotAddLink (
+ SHELL_FILE_HANDLE DotFileHandle,
+ IN UINT32 IdSource,
+ IN UINT32 IdTarget,
+ IN UINT16 Flags
+ );
+
+#endif // DOT_GENERATOR_H_
diff --git a/ShellPkg/Library/UefiShellAcpiViewCommandLib/DotGenerator.c b/ShellPkg/Library/UefiShellAcpiViewCommandLib/DotGenerator.c
new file mode 100644
index 0000000000000000000000000000000000000000..e19b17537ca85babca5cda38025ae95701ff794f
--- /dev/null
+++ b/ShellPkg/Library/UefiShellAcpiViewCommandLib/DotGenerator.c
@@ -0,0 +1,283 @@
+/** @file
+ Dot File Generator
+
+ Copyright (c) 2021, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PrintLib.h>
+#include "DotGenerator.h"
+#include "AcpiView.h"
+
+#define MAX_DOT_BUFFER_SIZE 128
+
+/**
+ Writes a Null terminated ASCII string to the dot file handle.
+
+ @param [in] DotFileHandle The handle of the dot file.
+ @param [in] String Null terminated ascii string.
+**/
+STATIC
+VOID
+DotWriteFile (
+ SHELL_FILE_HANDLE DotFileHandle,
+ IN CHAR8* String
+ )
+{
+ UINTN TransferBytes;
+ EFI_STATUS Status;
+
+ if (DotFileHandle == NULL) {
+ Print (L"ERROR: Failed to write to dot file\n");
+ ASSERT (0);
+ return;
+ }
+
+ TransferBytes = AsciiStrLen (String);
+ Status = ShellWriteFile (
+ DotFileHandle,
+ &TransferBytes,
+ String
+ );
+ ASSERT_EFI_ERROR (Status);
+ ASSERT (AsciiStrLen (String) == TransferBytes);
+}
+
+/**
+ Writes a new parameter to a previously started parameter list.
+
+ @param [in] DotFileHandle The handle of the dot file.
+ @param [in] Name Null terminated string of the parameter's name.
+ @param [in] Value Null terminated string of the parameter's value.
+ @param [in] Quoted True if value needs to be quoted.
+**/
+STATIC
+VOID
+DotAddParameter (
+ SHELL_FILE_HANDLE DotFileHandle,
+ IN CHAR16* Name,
+ IN CHAR16* Value,
+ IN BOOLEAN Quoted
+ )
+{
+ CHAR8 StringBuffer[MAX_DOT_BUFFER_SIZE];
+
+ ASSERT(DotFileHandle != NULL);
+
+ if (Quoted) {
+ AsciiSPrint (
+ StringBuffer,
+ sizeof (StringBuffer),
+ "[%s=\"%s\"]",
+ Name,
+ Value
+ );
+ } else {
+ AsciiSPrint (
+ StringBuffer,
+ sizeof (StringBuffer),
+ "[%s=%s]",
+ Name,
+ Value
+ );
+ }
+
+ DotWriteFile (DotFileHandle, StringBuffer);
+}
+
+/**
+ Writes the color argument of nodes or links according to flags.
+
+ @param [in] DotFileHandle The handle of the dot file.
+ @param [in] Flags Flags describing the color (one of DOT_COLOR_...)
+**/
+STATIC
+VOID
+WriteColor (
+ SHELL_FILE_HANDLE DotFileHandle,
+ IN UINT16 Flags
+ )
+{
+ ASSERT(DotFileHandle != NULL);
+
+ switch (Flags & DOT_COLOR_MASK) {
+ case DOT_COLOR_GRAY:
+ DotAddParameter (DotFileHandle, L"color", L"gray", FALSE);
+ break;
+ case DOT_COLOR_YELLOW:
+ DotAddParameter (DotFileHandle, L"color", L"yellow", FALSE);
+ break;
+ case DOT_COLOR_BLUE:
+ DotAddParameter (DotFileHandle, L"color", L"blue", FALSE);
+ break;
+ case DOT_COLOR_RED:
+ DotAddParameter (DotFileHandle, L"color", L"red", FALSE);
+ break;
+ case DOT_COLOR_BLACK:
+ default:
+ DotAddParameter (DotFileHandle, L"color", L"black", FALSE);
+ break;
+ }
+}
+
+/**
+ Opens a new dot file and writes a dot directional graph.
+
+ @param [in] FileName Null terminated unicode string.
+**/
+SHELL_FILE_HANDLE
+DotOpenNewFile (
+ IN CHAR16* FileName
+ )
+{
+ SHELL_FILE_HANDLE DotFileHandle;
+ EFI_STATUS Status;
+
+ Status = ShellOpenFileByName (
+ FileName,
+ &DotFileHandle,
+ EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE,
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ Print (L"ERROR: Couldn't open dot file");
+ return NULL;//Status;
+ }
+ Print (L"Creating DOT Graph in : %s... ", FileName);
+ DotWriteFile (DotFileHandle, "digraph {\n\trankdir=BT\n");
+ return DotFileHandle;
+}
+
+/**
+ Writes a dot graph footer and closes the dot file.
+
+ @param [in] DotFileHandle The handle of the dot file.
+**/
+VOID
+DotCloseFile (
+ SHELL_FILE_HANDLE DotFileHandle
+ )
+{
+ ASSERT(DotFileHandle != NULL);
+
+ DotWriteFile (DotFileHandle, "}\n");
+ ShellCloseFile (&DotFileHandle);
+ Print (L"Done.\n");
+}
+
+/**
+ Writes a line in the previously opened dot file describing a
+ new node.
+
+ @param [in] DotFileHandle The handle of the dot file.
+ @param [in] Id A unique identifier for the node.
+ @param [in] Flags Flags describing the node's characteristics.
+ @param [in] Label Label to be shown on the graph node.
+**/
+VOID
+DotAddNode (
+ SHELL_FILE_HANDLE DotFileHandle,
+ IN UINT32 Id,
+ IN UINT16 Flags,
+ IN CONST CHAR16* Label
+ )
+{
+ CHAR8 LineBuffer[64];
+ CHAR16 LabelBuffer[MAX_DOT_BUFFER_SIZE];
+
+ ASSERT ((Flags & ~DOT_BOX_FLAGS_MASK) == 0);
+ ASSERT(DotFileHandle != NULL);
+
+ AsciiSPrint (
+ LineBuffer,
+ sizeof (LineBuffer),
+ "\tx%x",
+ Id
+ );
+ DotWriteFile (DotFileHandle, LineBuffer);
+
+ switch (Flags & DOT_BOX_TYPE_MASK) {
+ case DOT_BOX_DIAMOND:
+ DotAddParameter (DotFileHandle, L"shape", L"diamond", FALSE);
+ break;
+ case DOT_BOX_SQUARE:
+ default:
+ DotAddParameter (DotFileHandle, L"shape", L"box", FALSE);
+ break;
+ }
+
+ if (Label != NULL) {
+ if ((Flags & DOT_BOX_ADD_ID_TO_LABEL) != 0) {
+ UnicodeSPrint (
+ LabelBuffer,
+ sizeof (LabelBuffer),
+ L"%s\\n0x%x",
+ Label,
+ Id
+ );
+ } else {
+ UnicodeSPrint (
+ LabelBuffer,
+ sizeof (LabelBuffer),
+ L"%s",
+ Label
+ );
+ }
+ DotAddParameter (DotFileHandle, L"label", LabelBuffer, TRUE);
+ }
+
+ WriteColor (DotFileHandle, Flags);
+ DotWriteFile (DotFileHandle, "\n");
+}
+
+/**
+ Writes a line in the previously opened dot file describing a
+ new link between two nodes.
+
+ @param [in] DotFileHandle The handle of the dot file.
+ @param [in] IdSource An identifier for the source node of the link.
+ @param [in] IdTarget An identifier for the target node of the link.
+ @param [in] Flags Flags describing the node's characteristics.
+**/
+VOID
+DotAddLink (
+ SHELL_FILE_HANDLE DotFileHandle,
+ IN UINT32 IdSource,
+ IN UINT32 IdTarget,
+ IN UINT16 Flags
+ )
+{
+ CHAR8 LineBuffer[64];
+
+ ASSERT(DotFileHandle != NULL);
+ ASSERT ((Flags & ~DOT_ARROW_FLAGS_MASK) == 0);
+
+ AsciiSPrint (
+ LineBuffer,
+ sizeof (LineBuffer),
+ "\tx%x -> x%x",
+ IdSource,
+ IdTarget
+ );
+ DotWriteFile (DotFileHandle, LineBuffer);
+
+ if ((Flags & DOT_ARROW_RANK_REVERSE) != 0) {
+ DotAddParameter (DotFileHandle, L"dir", L"back", FALSE);
+ }
+
+ switch (Flags & DOT_ARROW_TYPE_MASK) {
+ case DOT_ARROW_DOTTED:
+ DotAddParameter (DotFileHandle, L"style", L"dotted", FALSE);
+ break;
+ case DOT_ARROW_FULL:
+ default:
+ DotAddParameter (DotFileHandle, L"style", L"solid", FALSE);
+ break;
+ }
+
+ WriteColor (DotFileHandle, Flags);
+ DotWriteFile (DotFileHandle, "\n");
+}
--
Guid("CE165669-3EF3-493F-B85D-6190EE5B9759")