Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
4.5 KiB
209 lines
4.5 KiB
// Copyright 2019 The Prometheus Authors |
|
// Licensed under the Apache License, Version 2.0 (the "License"); |
|
// you may not use this file except in compliance with the License. |
|
// You may obtain a copy of the License at |
|
// |
|
// http://www.apache.org/licenses/LICENSE-2.0 |
|
// |
|
// Unless required by applicable law or agreed to in writing, software |
|
// distributed under the License is distributed on an "AS IS" BASIS, |
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
// See the License for the specific language governing permissions and |
|
// limitations under the License. |
|
|
|
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris |
|
|
|
package procfs |
|
|
|
import ( |
|
"bufio" |
|
"fmt" |
|
"os" |
|
"strconv" |
|
"strings" |
|
|
|
"golang.org/x/sys/unix" |
|
) |
|
|
|
// ProcMapPermissions contains permission settings read from /proc/[pid]/maps |
|
type ProcMapPermissions struct { |
|
// mapping has the [R]ead flag set |
|
Read bool |
|
// mapping has the [W]rite flag set |
|
Write bool |
|
// mapping has the [X]ecutable flag set |
|
Execute bool |
|
// mapping has the [S]hared flag set |
|
Shared bool |
|
// mapping is marked as [P]rivate (copy on write) |
|
Private bool |
|
} |
|
|
|
// ProcMap contains the process memory-mappings of the process, |
|
// read from /proc/[pid]/maps |
|
type ProcMap struct { |
|
// The start address of current mapping. |
|
StartAddr uintptr |
|
// The end address of the current mapping |
|
EndAddr uintptr |
|
// The permissions for this mapping |
|
Perms *ProcMapPermissions |
|
// The current offset into the file/fd (e.g., shared libs) |
|
Offset int64 |
|
// Device owner of this mapping (major:minor) in Mkdev format. |
|
Dev uint64 |
|
// The inode of the device above |
|
Inode uint64 |
|
// The file or psuedofile (or empty==anonymous) |
|
Pathname string |
|
} |
|
|
|
// parseDevice parses the device token of a line and converts it to a dev_t |
|
// (mkdev) like structure. |
|
func parseDevice(s string) (uint64, error) { |
|
toks := strings.Split(s, ":") |
|
if len(toks) < 2 { |
|
return 0, fmt.Errorf("unexpected number of fields") |
|
} |
|
|
|
major, err := strconv.ParseUint(toks[0], 16, 0) |
|
if err != nil { |
|
return 0, err |
|
} |
|
|
|
minor, err := strconv.ParseUint(toks[1], 16, 0) |
|
if err != nil { |
|
return 0, err |
|
} |
|
|
|
return unix.Mkdev(uint32(major), uint32(minor)), nil |
|
} |
|
|
|
// parseAddress just converts a hex-string to a uintptr |
|
func parseAddress(s string) (uintptr, error) { |
|
a, err := strconv.ParseUint(s, 16, 0) |
|
if err != nil { |
|
return 0, err |
|
} |
|
|
|
return uintptr(a), nil |
|
} |
|
|
|
// parseAddresses parses the start-end address |
|
func parseAddresses(s string) (uintptr, uintptr, error) { |
|
toks := strings.Split(s, "-") |
|
if len(toks) < 2 { |
|
return 0, 0, fmt.Errorf("invalid address") |
|
} |
|
|
|
saddr, err := parseAddress(toks[0]) |
|
if err != nil { |
|
return 0, 0, err |
|
} |
|
|
|
eaddr, err := parseAddress(toks[1]) |
|
if err != nil { |
|
return 0, 0, err |
|
} |
|
|
|
return saddr, eaddr, nil |
|
} |
|
|
|
// parsePermissions parses a token and returns any that are set. |
|
func parsePermissions(s string) (*ProcMapPermissions, error) { |
|
if len(s) < 4 { |
|
return nil, fmt.Errorf("invalid permissions token") |
|
} |
|
|
|
perms := ProcMapPermissions{} |
|
for _, ch := range s { |
|
switch ch { |
|
case 'r': |
|
perms.Read = true |
|
case 'w': |
|
perms.Write = true |
|
case 'x': |
|
perms.Execute = true |
|
case 'p': |
|
perms.Private = true |
|
case 's': |
|
perms.Shared = true |
|
} |
|
} |
|
|
|
return &perms, nil |
|
} |
|
|
|
// parseProcMap will attempt to parse a single line within a proc/[pid]/maps |
|
// buffer. |
|
func parseProcMap(text string) (*ProcMap, error) { |
|
fields := strings.Fields(text) |
|
if len(fields) < 5 { |
|
return nil, fmt.Errorf("truncated procmap entry") |
|
} |
|
|
|
saddr, eaddr, err := parseAddresses(fields[0]) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
perms, err := parsePermissions(fields[1]) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
offset, err := strconv.ParseInt(fields[2], 16, 0) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
device, err := parseDevice(fields[3]) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
inode, err := strconv.ParseUint(fields[4], 10, 0) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
pathname := "" |
|
|
|
if len(fields) >= 5 { |
|
pathname = strings.Join(fields[5:], " ") |
|
} |
|
|
|
return &ProcMap{ |
|
StartAddr: saddr, |
|
EndAddr: eaddr, |
|
Perms: perms, |
|
Offset: offset, |
|
Dev: device, |
|
Inode: inode, |
|
Pathname: pathname, |
|
}, nil |
|
} |
|
|
|
// ProcMaps reads from /proc/[pid]/maps to get the memory-mappings of the |
|
// process. |
|
func (p Proc) ProcMaps() ([]*ProcMap, error) { |
|
file, err := os.Open(p.path("maps")) |
|
if err != nil { |
|
return nil, err |
|
} |
|
defer file.Close() |
|
|
|
maps := []*ProcMap{} |
|
scan := bufio.NewScanner(file) |
|
|
|
for scan.Scan() { |
|
m, err := parseProcMap(scan.Text()) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
maps = append(maps, m) |
|
} |
|
|
|
return maps, nil |
|
}
|
|
|