55package  main
66
77import  (
8+ 	"bytes" 
89	"fmt" 
10+ 	"io" 
911	"os" 
1012	"path/filepath" 
1113
@@ -14,6 +16,12 @@ import (
1416	"github.com/olekukonko/tablewriter" 
1517)
1618
19+ const  (
20+ 	// chunkSize represents the default size of memory chunk (in bytes) 
21+ 	// to read for each output line when printing memory pages content in hexdump-like format. 
22+ 	chunkSize  =  16 
23+ )
24+ 
1725// Display processes memory sizes within the given container checkpoints. 
1826func  showProcessMemorySizeTables (tasks  []task ) error  {
1927	// Initialize the table 
@@ -81,3 +89,93 @@ func showProcessMemorySizeTables(tasks []task) error {
8189
8290	return  nil 
8391}
92+ 
93+ func  printProcessMemoryPages (task  task ) error  {
94+ 	memReader , err  :=  crit .NewMemoryReader (
95+ 		filepath .Join (task .outputDir , metadata .CheckpointDirectory ),
96+ 		pID , pageSize ,
97+ 	)
98+ 	if  err  !=  nil  {
99+ 		return  err 
100+ 	}
101+ 
102+ 	// Write the output to stdout by default 
103+ 	var  output  io.Writer  =  os .Stdout 
104+ 	var  compact  bool 
105+ 
106+ 	if  outputFilePath  !=  ""  {
107+ 		// Write to ouput to file if --output is provided 
108+ 		output , err  =  os .Create (outputFilePath )
109+ 		if  err  !=  nil  {
110+ 			return  err 
111+ 		}
112+ 	} else  {
113+ 		compact  =  true  // Use a compact format when writing the output to stdout 
114+ 		fmt .Printf ("\n Displaying memory pages content for Process ID: %d from checkpoint: %s\n \n " , pID , task .checkpointFilePath )
115+ 	}
116+ 
117+ 	fmt .Fprintf (output , "Address           Hexadecimal                                       ASCII            \n " )
118+ 	fmt .Fprintf (output , "-------------------------------------------------------------------------------------\n " )
119+ 
120+ 	pagemapEntries  :=  memReader .GetPagemapEntries ()
121+ 	for  _ , entry  :=  range  pagemapEntries  {
122+ 		start  :=  entry .GetVaddr ()
123+ 		end  :=  start  +  (uint64 (pageSize ) *  uint64 (entry .GetNrPages ()))
124+ 		buf , err  :=  memReader .GetMemPages (start , end )
125+ 		if  err  !=  nil  {
126+ 			return  err 
127+ 		}
128+ 
129+ 		hexdump (output , buf , start , compact )
130+ 	}
131+ 	return  nil 
132+ }
133+ 
134+ // hexdump generates a hexdump of the buffer 'buf' starting at the virtual address 'start' 
135+ // and writes the output to 'out'. If compact is true, consecutive duplicate rows will be represented 
136+ // with an asterisk (*). 
137+ func  hexdump (out  io.Writer , buf  * bytes.Buffer , vaddr  uint64 , compact  bool ) {
138+ 	prevAscii  :=  "" 
139+ 	duplicate  :=  0 
140+ 	for  buf .Len () >  0  {
141+ 		row  :=  buf .Next (chunkSize )
142+ 		hex , ascii  :=  generateHexAndAscii (row )
143+ 
144+ 		if  compact  {
145+ 			if  prevAscii  ==  ascii  {
146+ 				if  duplicate  ==  1  {
147+ 					fmt .Fprint (out , "*\n " )
148+ 				}
149+ 				duplicate ++ 
150+ 			} else  {
151+ 				printHexdumpRow (out , vaddr , hex , ascii )
152+ 				duplicate  =  0 
153+ 			}
154+ 		} else  {
155+ 			printHexdumpRow (out , vaddr , hex , ascii )
156+ 		}
157+ 
158+ 		vaddr  +=  chunkSize 
159+ 		prevAscii  =  ascii 
160+ 	}
161+ }
162+ 
163+ func  printHexdumpRow (out  io.Writer , vaddr  uint64 , hex  string , ascii  string ) {
164+ 	fmt .Fprintf (out , "%016x  %s |%s|\n " , vaddr , hex , ascii )
165+ }
166+ 
167+ // generateHexAndAscii takes a byte slice and generates its hexadecimal and ASCII representations. 
168+ func  generateHexAndAscii (data  []byte ) (hex , ascii  string ) {
169+ 	s  :=  string (data )
170+ 	for  i  :=  0 ; i  <  len (s ); i ++  {
171+ 		if  s [i ] <  32  ||  s [i ] >=  127  {
172+ 			ascii  +=  "." 
173+ 			hex  +=  fmt .Sprintf ("%02x " , s [i ])
174+ 		} else  {
175+ 			ascii  +=  string (s [i ])
176+ 			hex  +=  fmt .Sprintf ("%02x " , s [i ])
177+ 		}
178+ 	}
179+ 
180+ 	return  hex , ascii 
181+ }
0 commit comments