diff --git a/maps/maps.go b/maps/maps.go new file mode 100644 index 0000000..da2a31b --- /dev/null +++ b/maps/maps.go @@ -0,0 +1,111 @@ +// +// maps.Maps describes data in /proc//maps. +// +// Use maps.New() to create a new maps.Maps object +// from data in a path. +// +package maps + +// +// Copyright Arkady Maisnikov (jandre@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +import ( + "bufio" + "github.com/jandre/procfs/util" + "log" + "os" + "strconv" + "strings" +) + +// +// Abstraction for /proc//maps +// +type Maps struct { + AddressStart uint64 // This is the starting ... + AddressEnd uint64 // and ending address of the region in the process's address space + Perms string // Describes how pages in the region can be accessed. + Offset uint64 // If the region was mapped from a file (using mmap), this is the offset in the file where the mapping begins. If the memory was not mapped from a file, it's just 0. + Device string // If the region was mapped from a file, this is the major and minor device number (in hex) where the file lives. + Inode int // If the region was mapped from a file, this is the file number. + Pathname string // If the region was mapped from a file, this is the name of the file. +} + +type ProcMap struct { + AddressRange string // This is the starting and ending address of the region in the process's address space + Perms string // Describes how pages in the region can be accessed. + Offset string // If the region was mapped from a file (using mmap), this is the offset in the file where the mapping begins. If the memory was not mapped from a file, it's just 0. + Device string // If the region was mapped from a file, this is the major and minor device number (in hex) where the file lives. + Inode int // If the region was mapped from a file, this is the file number. + Pathname string // If the region was mapped from a file, this is the name of the file. +} + +func New(path string) ([]*Maps, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + + scanner := bufio.NewScanner(f) + var maps []*Maps + + var columns []string + for scanner.Scan() { + procMap := &ProcMap{} + line := scanner.Text() + columns = strings.Split(line, " ") + err = util.ParseStringsIntoStruct(procMap, columns) + var offset uint64 = 0 + if err != nil { + break + } + offset, err = strconv.ParseUint(procMap.Offset, 16, 64) + if err != nil { + break + } + var addressStart uint64 + var addressEnd uint64 + addressRange := strings.Split(procMap.AddressRange, "-") + addressStart, err = strconv.ParseUint(addressRange[0], 16, 64) + if err != nil { + break + } + addressEnd, err = strconv.ParseUint(addressRange[1], 16, 64) + if err != nil { + break + } + var newMap *Maps = &Maps{Perms: procMap.Perms, + AddressStart: addressStart, + AddressEnd: addressEnd, + Offset: offset, + Device: procMap.Device, + Inode: procMap.Inode, + Pathname: procMap.Pathname} + maps = append(maps, newMap) + } + if err != nil { + log.Println("Failed to parse", columns, err) + } + + return maps, err +} diff --git a/maps/maps_test.go b/maps/maps_test.go new file mode 100644 index 0000000..36b8c32 --- /dev/null +++ b/maps/maps_test.go @@ -0,0 +1,23 @@ +package maps + +import ( + "log" + "testing" +) + +func TestParsingMaps(t *testing.T) { + m, err := New("./testfiles/maps") + + if err != nil { + t.Fatal("Got error", err) + } + + if m == nil { + t.Fatal("maps is missing") + } + log.Println("maps", m) + + if len(m) != 19 { + t.Fatal("Expected 19 entries") + } +} diff --git a/maps/testfiles/maps b/maps/testfiles/maps new file mode 100644 index 0000000..ac687eb --- /dev/null +++ b/maps/testfiles/maps @@ -0,0 +1,19 @@ +5629afed1000-5629afed9000 r-xp 00000000 103:05 1439104 /bin/cat +5629b00d8000-5629b00d9000 r--p 00007000 103:05 1439104 /bin/cat +5629b00d9000-5629b00da000 rw-p 00008000 103:05 1439104 /bin/cat +5629b0b6b000-5629b0b8c000 rw-p 00000000 00:00 0 [heap] +7fa5a44d7000-7fa5a48e8000 r--p 00000000 103:05 1835379 /usr/lib/locale/locale-archive +7fa5a48e8000-7fa5a4acf000 r-xp 00000000 103:05 1570001 /lib/x86_64-linux-gnu/libc-2.27.so +7fa5a4acf000-7fa5a4ccf000 ---p 001e7000 103:05 1570001 /lib/x86_64-linux-gnu/libc-2.27.so +7fa5a4ccf000-7fa5a4cd3000 r--p 001e7000 103:05 1570001 /lib/x86_64-linux-gnu/libc-2.27.so +7fa5a4cd3000-7fa5a4cd5000 rw-p 001eb000 103:05 1570001 /lib/x86_64-linux-gnu/libc-2.27.so +7fa5a4cd5000-7fa5a4cd9000 rw-p 00000000 00:00 0 +7fa5a4cd9000-7fa5a4d00000 r-xp 00000000 103:05 1569997 /lib/x86_64-linux-gnu/ld-2.27.so +7fa5a4ebd000-7fa5a4ee1000 rw-p 00000000 00:00 0 +7fa5a4f00000-7fa5a4f01000 r--p 00027000 103:05 1569997 /lib/x86_64-linux-gnu/ld-2.27.so +7fa5a4f01000-7fa5a4f02000 rw-p 00028000 103:05 1569997 /lib/x86_64-linux-gnu/ld-2.27.so +7fa5a4f02000-7fa5a4f03000 rw-p 00000000 00:00 0 +7fff3930d000-7fff3932e000 rw-p 00000000 00:00 0 [stack] +7fff393d1000-7fff393d4000 r--p 00000000 00:00 0 [vvar] +7fff393d4000-7fff393d6000 r-xp 00000000 00:00 0 [vdso] +ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] diff --git a/process.go b/process.go index b515460..166be4a 100644 --- a/process.go +++ b/process.go @@ -30,6 +30,7 @@ import ( "strings" "github.com/jandre/procfs/limits" + "github.com/jandre/procfs/maps" "github.com/jandre/procfs/stat" "github.com/jandre/procfs/statm" "github.com/jandre/procfs/status" @@ -48,11 +49,11 @@ type Process struct { prefix string // directory path, e.g. /proc/ stat *stat.Stat // Status information from /proc/pid/stat - see Stat() statm *statm.Statm // Memory Status information from /proc/pid/statm - see Statm() - status *status.Status //Status information from /proc/pid/status + status *status.Status // Status information from /proc/pid/status limits *limits.Limits // Per process rlimit settings from /proc/pid/limits - see Limits() loginuid *int // Maybe loginuid from /proc/pid/loginuid - see Loginuid() sessionid *int // Maybe sessionid from /proc/pid/sessionid- see Sessionid() - + maps []*maps.Maps // Process adderss space } // @@ -103,6 +104,7 @@ func NewProcessFromPath(pid int, prefix string, lazy bool) (*Process, error) { process.Limits() process.Loginuid() process.Sessionid() + process.Maps() } return process, nil @@ -271,3 +273,17 @@ func (p *Process) readEnviron() { } } } + +// +// Parser for /proc//maps +// +func (p *Process) Maps() ([]*maps.Maps, error) { + var err error + if p.stat == nil { + if p.maps, err = maps.New(path.Join(p.prefix, "maps")); err != nil { + return nil, err + } + } + + return p.maps, nil +}