// Copyright 2020 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.

//go:build !windows
// +build !windows

package procfs

import (
	
	
	
	
	
	

	
)

var (
	// Match the header line before each mapped zone in `/proc/pid/smaps`.
	procSMapsHeaderLine = regexp.MustCompile(`^[a-f0-9].*$`)
)

type ProcSMapsRollup struct {
	// Amount of the mapping that is currently resident in RAM.
	Rss uint64
	// Process's proportional share of this mapping.
	Pss uint64
	// Size in bytes of clean shared pages.
	SharedClean uint64
	// Size in bytes of dirty shared pages.
	SharedDirty uint64
	// Size in bytes of clean private pages.
	PrivateClean uint64
	// Size in bytes of dirty private pages.
	PrivateDirty uint64
	// Amount of memory currently marked as referenced or accessed.
	Referenced uint64
	// Amount of memory that does not belong to any file.
	Anonymous uint64
	// Amount would-be-anonymous memory currently on swap.
	Swap uint64
	// Process's proportional memory on swap.
	SwapPss uint64
}

// ProcSMapsRollup reads from /proc/[pid]/smaps_rollup to get summed memory information of the
// process.
//
// If smaps_rollup does not exists (require kernel >= 4.15), the content of /proc/pid/smaps will
// we read and summed.
func ( Proc) () (ProcSMapsRollup, error) {
	,  := util.ReadFileNoStat(.path("smaps_rollup"))
	if  != nil && os.IsNotExist() {
		return .procSMapsRollupManual()
	}
	if  != nil {
		return ProcSMapsRollup{}, 
	}

	 := strings.Split(string(), "\n")
	 := ProcSMapsRollup{}

	// skip first line which don't contains information we need
	 = [1:]
	for ,  := range  {
		if  == "" {
			continue
		}

		if  := .parseLine();  != nil {
			return ProcSMapsRollup{}, 
		}
	}

	return , nil
}

// Read /proc/pid/smaps and do the roll-up in Go code.
func ( Proc) () (ProcSMapsRollup, error) {
	,  := os.Open(.path("smaps"))
	if  != nil {
		return ProcSMapsRollup{}, 
	}
	defer .Close()

	 := ProcSMapsRollup{}
	 := bufio.NewScanner()

	for .Scan() {
		 := .Text()

		if procSMapsHeaderLine.MatchString() {
			continue
		}

		if  := .parseLine();  != nil {
			return ProcSMapsRollup{}, 
		}
	}

	return , nil
}

func ( *ProcSMapsRollup) ( string) error {
	 := strings.SplitN(, ":", 2)
	if len() != 2 {
		return errors.New("invalid net/dev line, missing colon")
	}

	 := [0]
	if  == "VmFlags" {
		return nil
	}

	 := strings.TrimSpace([1])
	 = strings.TrimSuffix(, " kB")

	,  := strconv.ParseUint(, 10, 64)
	if  != nil {
		return 
	}
	 :=  * 1024

	.addValue(, )

	return nil
}

func ( *ProcSMapsRollup) ( string,  uint64) {
	switch  {
	case "Rss":
		.Rss += 
	case "Pss":
		.Pss += 
	case "Shared_Clean":
		.SharedClean += 
	case "Shared_Dirty":
		.SharedDirty += 
	case "Private_Clean":
		.PrivateClean += 
	case "Private_Dirty":
		.PrivateDirty += 
	case "Referenced":
		.Referenced += 
	case "Anonymous":
		.Anonymous += 
	case "Swap":
		.Swap += 
	case "SwapPss":
		.SwapPss += 
	}
}