Skip to content

Instantly share code, notes, and snippets.

@ericlagergren
Last active February 12, 2016 09:57
Show Gist options
  • Save ericlagergren/42b050de2dde114e7737 to your computer and use it in GitHub Desktop.
Save ericlagergren/42b050de2dde114e7737 to your computer and use it in GitHub Desktop.
Read /proc/$$/maps into something workable.
// +build ignore
package main
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"strconv"
"strings"
)
func main() {
for _, m := range ParseMaps() {
fmt.Printf("%#v\n", m)
}
}
func slurp(name string) []byte {
file, err := os.Open(name)
if err != nil {
log.Fatalln(err)
}
defer file.Close()
buf, err := ioutil.ReadAll(file)
if err != nil {
log.Fatalln(err)
}
return buf
}
// ParseMaps parses /proc/$$/maps into a useable data structure.
func ParseMaps() (maps []Map) {
// filename is "/proc/$$/maps"
file := "/proc/" + strconv.Itoa(os.Getpid()) + "/maps"
lines := bytes.Split(slurp(file), []byte{'\n'})
var m Map
for _, line := range lines {
if line == nil || len(line) == 0 {
continue
}
parts := bytes.Split(line, []byte{' '})
// 6 parts minimum, but no max since sometimes
// there's a big space between inode and path.
if len(parts) < 6 {
return nil
}
// Convert the address ranges from hex to uintptr.
addr := bytes.Split(parts[0], []byte{'-'})
m.Start = hexToUintptr(addr[0])
m.End = hexToUintptr(addr[1])
// Convert 'rwxp' to permissions bitmask.
for _, c := range parts[1] {
switch c {
case 'r':
m.Perms |= Read
case 'w':
m.Perms |= Write
case 'x':
m.Perms |= Exec
// No case 's' because it defaults to false.
case 'p':
m.Private = true
}
}
m.Offset = hexToUintptr(parts[2])
// Split dev into Major:Minor parts.
dev := bytes.Split(parts[3], []byte{':'})
m.Dev.Maj = parseUint(dev[0])
m.Dev.Min = parseUint(dev[1])
m.Inode = parseUint(parts[4])
m.Path = string(parts[len(parts)-1])
maps = append(maps, m)
}
return maps
}
const (
None = 0 // 0x0
Read = 1 << iota // 0x1
Write // 0x2
Exec // 0x4
)
// Map is a mapped memory region, found in /proc/$$/maps
// See: mmap(2)
type Map struct {
Start uintptr // Beginning memory address.
End uintptr // Ending memory address.
Perms uint8 // Memory protection bitmask.
Private bool // true if the mapping is private (copy on write).
Offset uintptr // Offset where mapping begins.
Dev struct {
Maj, Min uint64
} // Major and minor device number.
Inode uint64 // If mapped from a file, the file's inode.
// If mapped from a file, the file's path. Special values
// include [stack], [heap], and [vsdo]. See related methods.
Path string
}
// IsStack returns true if the mapping points to the stack.
func (m Map) IsStack() bool {
return m.Path == "[stack]"
}
// IsHeap returns true if the mapping points to the heap.
func (m Map) IsHeap() bool {
return m.Path == "[heap]"
}
// IsVSDO returns true if the mapping points to a virtual dynamically linked
// shared object.
func (m Map) IsVSDO() bool {
return m.Path == "[vsdo]"
}
// ErrVersion is returned from the ThreadID method if the mapping
// does not have a thread ID. (Usually means the linux version is
// too old.)
var ErrVersion = errors.New("thread id needs linux >= 3.4")
// ThreadID returns the thread (mapping) ID that corresponds
// to the /proc/$$/task/[id] path. It returns an error if the
// mapping is either not a stack or does not have a thread id.
func (m Map) ThreadID() (int, error) {
if !m.IsStack() {
return 0, ErrVersion
}
i := strings.IndexByte(m.Path, ':')
if i < 0 {
return 0, ErrVersion
}
return strconv.Atoi(m.Path[i+1 : len(m.Path)-1])
}
// hexToUintptr converts b into a uintptr.
// It's optimized to assume the input will not be invalid.
// (I.e., that /proc/$$/maps won't produce a garbage value.)
func hexToUintptr(b []byte) (n uintptr) {
for _, d := range b {
switch {
case '0' <= d && d <= '9':
n += uintptr(d - '0')
case 'a' <= d && d <= 'z':
n += uintptr(d - 'a' + 10)
case 'A' <= d && d <= 'Z':
n += uintptr(d - 'A' + 10)
default:
return 0
}
n *= 16
}
return n
}
// parseUint parses b into a uint64. See hexToUintptr for more.
func parseUint(b []byte) (n uint64) {
for _, d := range b {
switch {
case '0' <= d && d <= '9':
n += uint64(d - '0')
case 'a' <= d && d <= 'z':
n += uint64(d - 'a' + 10)
case 'A' <= d && d <= 'Z':
n += uint64(d - 'A' + 10)
default:
return 0
}
n *= 10
}
return n
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment