Skip to content

Commit

Permalink
crit: add functions to retrieve memory page contents
Browse files Browse the repository at this point in the history
This commit add  GetMemPages function to retrieve the memory pages
associated with a process.

Signed-off-by: Kouame Behouba Manasse <[email protected]>
  • Loading branch information
behouba committed Jun 2, 2023
1 parent 748b90b commit 380a8f2
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 0 deletions.
172 changes: 172 additions & 0 deletions crit/mempages.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package crit

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

"github.com/checkpoint-restore/go-criu/v6/crit/images/mm"
"github.com/checkpoint-restore/go-criu/v6/crit/images/pagemap"
"github.com/checkpoint-restore/go-criu/v6/crit/images/vma"
)

const (
pageSize = 4096

vmaAreaVvar = 1 << 12 // VMA_AREA_VVAR
vmaAreaVsyscall = 1 << 2 // VMA_AREA_VSYSCALL
)

// GetMemPages retrieves memory pages associated with a pid.
func GetMemPages(dir string, pid int) ([]byte, error) {
mmImg, err := getImg(filepath.Join(dir, fmt.Sprintf("mm-%d.img", pid)), &mm.MmEntry{})
if err != nil {
return nil, err
}

vmas := mmImg.Entries[0].Message.(*mm.MmEntry).GetVmas()

var buff bytes.Buffer
for _, vma := range vmas {
size := *vma.End - *vma.Start
pages, err := generateMemoryChunk(dir, pid, vma, size)
if err != nil {
return nil, err
}
buff.Write(pages)
}

return buff.Bytes(), nil
}

// generateMemoryChunk generates the memory chunk from a given VMA.
func generateMemoryChunk(dir string, pid int, vma *vma.VmaEntry, size uint64) ([]byte, error) {
if size == 0 {
return nil, nil
}

// TODO: Is this the right way ? since we are in the context of container
if *vma.Status&vmaAreaVvar != 0 {
return bytes.Repeat([]byte("\x00"), int(pageSize)), nil
} else if *vma.Status&vmaAreaVsyscall != 0 {
return bytes.Repeat([]byte("\x00"), int(pageSize)), nil
}

pagemapImg, err := getImg(filepath.Join(dir, fmt.Sprintf("pagemap-%d.img", pid)), &pagemap.PagemapHead{})
if err != nil {
return nil, err
}

pagesID := pagemapImg.Entries[0].Message.(*pagemap.PagemapHead).GetPagesId()

pagemapEntries := make([]*pagemap.PagemapEntry, 0)

for _, entry := range pagemapImg.Entries[1:] {
pagemapEntries = append(pagemapEntries, entry.Message.(*pagemap.PagemapEntry))
}

pagesFile, err := os.Open(filepath.Join(dir, fmt.Sprintf("pages-%d.img", pagesID)))
if err != nil {
return nil, err
}

defer pagesFile.Close()

start := *vma.Start
end := *vma.Start + size

startPage := start / pageSize
endPage := end / pageSize

var buff bytes.Buffer

for pageNo := startPage; pageNo <= endPage; pageNo++ {
var pageData []byte

pageMem, err := getPage(dir, int(pagesID), pageNo, pagemapEntries)
if err != nil {
return nil, err
}

if pagesFile != nil {
pageData = make([]byte, pageSize)
pagesFile.Read(pageData)
}

if pageMem != nil {
pageData = pageMem
}

if pageMem == nil {
pageData = bytes.Repeat([]byte("\x00"), int(pageSize))
}

var nSkip, nRead uint64

if pageNo == startPage {
nSkip = start - pageNo*pageSize
if startPage == endPage {
nRead = size
} else {
nRead = pageSize - nSkip
}
} else if pageNo == endPage {
nSkip = 0
nRead = end - pageNo*pageSize
} else {
nSkip = 0
nRead = pageSize
}

buff.Write(pageData[nSkip : nSkip+nRead])

}

return buff.Bytes(), nil
}

// getPage try to retrieves the page data for a given page number.
func getPage(dir string, pagesID int, pageNo uint64, pagemapEntries []*pagemap.PagemapEntry) ([]byte, error) {
var off uint64 = 0

for _, m := range pagemapEntries {
found := false

for i := 0; i < int(*m.NrPages); i++ {
if *m.Vaddr+uint64(i)*pageSize == pageNo*pageSize {
found = true
break
}
off += 1
}

if !found {
continue
}

f, err := os.Open(filepath.Join(dir, fmt.Sprintf("pages-%d.img", pagesID)))
if err != nil {
return nil, err
}

defer f.Close()

_, err = f.Seek(int64(off*pageSize), 0)
if err != nil {
return nil, err
}

buff := make([]byte, pageSize)

_, err = f.Read(buff)
if err != nil {
return nil, err
}

return buff, nil

}

return nil, nil
}
45 changes: 45 additions & 0 deletions crit/mempages_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package crit

import (
"fmt"
"io/ioutil"
"regexp"
"strconv"
"testing"
)

func TestGetMemPages(t *testing.T) {

dir := "test-imgs"
pid := 0

pagemapFilePattern := `pagemap-(\d+)\.img`

files, err := ioutil.ReadDir(dir)
if err != nil {
t.Fatalf("Failed to read directory: %v\n", err)
}

re := regexp.MustCompile(pagemapFilePattern)

for _, file := range files {
if re.MatchString(file.Name()) {
numberStr := re.FindStringSubmatch(file.Name())[1]
fmt.Println("Number: ", re.FindStringSubmatch(file.Name()))
pid, err = strconv.Atoi(numberStr)
if err != nil {
t.Fatalf("Failed to convert number: %v\n", err)
}
break
}
}

pages, err := GetMemPages(dir, pid)
if err != nil {
t.Errorf("GetMemPages returned an error: %v", err)
}

if len(pages) == 0 {
t.Error("Expected non-empty pages slice")
}
}

0 comments on commit 380a8f2

Please sign in to comment.