mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-01 14:47:38 +00:00
7f4fb34182
It is better developing vmctl tool in VictoriaMetrics repository, so it could be released together with the rest of vmutils tools such as vmalert, vmagent, vmbackup, vmrestore and vmauth.
209 lines
4.5 KiB
Go
209 lines
4.5 KiB
Go
// 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
|
|
}
|