Skip to content

Commit

Permalink
feat: add memparse sub-command
Browse files Browse the repository at this point in the history
This commit introduces a new sub-command `memparse` for analyzing
memory pages of processes. Using `memparse` without parameters will
display an overview of memory size of each process inside a container
checkpoint.

Signed-off-by: Kouame Behouba Manasse <behouba@gmail.com>
  • Loading branch information
behouba committed Aug 5, 2023
1 parent 002ac82 commit 119745a
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 0 deletions.
33 changes: 33 additions & 0 deletions checkpointctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ func main() {
inspectCommand := setupInspect()
rootCommand.AddCommand(inspectCommand)

memparseCommand := setupMemParse()
rootCommand.AddCommand(memparseCommand)

rootCommand.Version = version

if err := rootCommand.Execute(); err != nil {
Expand Down Expand Up @@ -263,3 +266,33 @@ func cleanupTasks(tasks []task) {
}
}
}

func setupMemParse() *cobra.Command {
cmd := &cobra.Command{
Use: "memparse",
Short: "Analyze container checkpoint memory",
RunE: memparse,
Args: cobra.MinimumNArgs(1),
}

return cmd
}

func memparse(cmd *cobra.Command, args []string) error {
requiredFiles := []string{
metadata.SpecDumpFile, metadata.ConfigDumpFile,
filepath.Join(metadata.CheckpointDirectory, "pstree.img"),
filepath.Join(metadata.CheckpointDirectory, "core-"),
filepath.Join(metadata.CheckpointDirectory, "pagemap-"),
filepath.Join(metadata.CheckpointDirectory, "pages-"),
filepath.Join(metadata.CheckpointDirectory, "mm-"),
}

tasks, err := createTasks(args, requiredFiles)
if err != nil {
return err
}
defer cleanupTasks(tasks)

return showProcessMemorySizeTables(tasks)
}
83 changes: 83 additions & 0 deletions memparse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: Apache-2.0

// This file is used to handle memory pages analysis of container checkpoints

package main

import (
"fmt"
"os"
"path/filepath"

metadata "github.com/checkpoint-restore/checkpointctl/lib"
"github.com/checkpoint-restore/go-criu/v6/crit"
"github.com/olekukonko/tablewriter"
)

// Display processes memory sizes within the given container checkpoints.
func showProcessMemorySizeTables(tasks []task) error {
// Initialize the table
table := tablewriter.NewWriter(os.Stdout)
header := []string{
"PID",
"Process name",
"Memory size",
}
table.SetHeader(header)
table.SetAutoMergeCells(false)
table.SetRowLine(true)

// Function to recursively traverse the process tree and populate the table rows
var traverseTree func(*crit.PsTree, string) error
traverseTree = func(root *crit.PsTree, checkpointOutputDir string) error {
memReader, err := crit.NewMemoryReader(
filepath.Join(checkpointOutputDir, metadata.CheckpointDirectory),
root.PID, pageSize,
)
if err != nil {
return err
}

pagemapEntries := memReader.GetPagemapEntries()

var memSize int64

for _, entry := range pagemapEntries {
memSize += int64(*entry.NrPages) * int64(pageSize)
}

table.Append([]string{
fmt.Sprintf("%d", root.PID),
root.Comm,
metadata.ByteToString(memSize),
})

for _, child := range root.Children {
if err := traverseTree(child, checkpointOutputDir); err != nil {
return err
}
}
return nil
}

for _, task := range tasks {
// Clear the table before processing each checkpoint task
table.ClearRows()

c := crit.New(nil, nil, filepath.Join(task.outputDir, "checkpoint"), false, false)
psTree, err := c.ExplorePs()
if err != nil {
return fmt.Errorf("failed to get process tree: %w", err)
}

// Populate the table rows
if err := traverseTree(psTree, task.outputDir); err != nil {
return err
}

fmt.Printf("\nDisplaying processes memory sizes from %s\n\n", task.checkpointFilePath)
table.Render()
}

return nil
}

0 comments on commit 119745a

Please sign in to comment.