cadvisor/utils/procfs/scheddebug.go
Vishnu Kannan 3e390e0f33 Use glog instead of 'log' library.
Docker-DCO-1.1-Signed-off-by: Vishnu Kannan <vishnuk@google.com> (github: vishh)
2014-08-07 18:38:58 +00:00

205 lines
6.1 KiB
Go

// Copyright 2014 Google Inc. All Rights Reserved.
//
// 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.
package procfs
import (
"bufio"
"fmt"
"strings"
"github.com/google/cadvisor/utils/fs"
)
type SchedulerLoadReader interface {
// Load() returns the load of each core of given container. If there's
// no information of given container, it should return nil, nil. If the
// returned load is not nil, the number of elements in the returned
// slice must be the same as the number of cores on the machine. Each
// element in the returned slices represents number of tasks/threads in
// the container and waiting for the CPU, i.e. number of runnable
// tasks.
Load(container string) ([]int, error)
AllContainers() ([]string, error)
}
func NewSchedulerLoadReader() (SchedulerLoadReader, error) {
schedDebug, err := fs.Open("/proc/sched_debug")
if err != nil {
return nil, err
}
scanner := bufio.NewScanner(schedDebug)
stateMachine := newSchedDebugReader()
for scanner.Scan() {
line := scanner.Text()
err = stateMachine.ProcessLine(line)
if err != nil {
return nil, err
}
}
if err = scanner.Err(); err != nil {
return nil, err
}
return stateMachine.Load()
}
type schedDebugReaderStateMachine struct {
currentState schedDebugReaderState
context *schedDebugContext
}
func newSchedDebugReader() *schedDebugReaderStateMachine {
return &schedDebugReaderStateMachine{
currentState: &schedDebugReaderStateReadingVersion{},
context: &schedDebugContext{
loadMap: make(map[string][]int, 8),
},
}
}
func (self *schedDebugReaderStateMachine) ProcessLine(line string) error {
nextState, err := self.currentState.Transit(self.context, line)
if err != nil {
return err
}
self.currentState = nextState
return nil
}
func (self *schedDebugReaderStateMachine) Load() (SchedulerLoadReader, error) {
return self.context.loadMap, nil
}
type schedDebugContext struct {
loadMap simpleSchedulerLoadReader
numCores int
}
// Key: container name
// Value: number of runnable processes in each core
type simpleSchedulerLoadReader map[string][]int
func (self simpleSchedulerLoadReader) Load(container string) ([]int, error) {
if load, ok := self[container]; ok {
return load, nil
}
return nil, nil
}
func (self simpleSchedulerLoadReader) AllContainers() ([]string, error) {
ret := make([]string, 0, len(self))
for c := range self {
ret = append(ret, c)
}
return ret, nil
}
// This interface is used to represent a state when reading sched_debug. The
// reader of sched_debug is a state machine and will transit to another state
// (and change its behavior accordingly) when a certain line is read.
// The state machine is implemented using the State Pattern.
type schedDebugReaderState interface {
// Transit() consumes a line from sched_debug and returns a new state
// for the next line. It may change the context accordingly.
Transit(context *schedDebugContext, line string) (schedDebugReaderState, error)
}
// State: Reading version
// In this state, the state machine is waiting for the version of the Sched Debug message.
// If the version is supported, it will transit to WaitingHeader state.
// Otherwise, it will report error
type schedDebugReaderStateReadingVersion struct {
}
func (self *schedDebugReaderStateReadingVersion) Transit(context *schedDebugContext, line string) (schedDebugReaderState, error) {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "Sched Debug Version") {
if !strings.HasPrefix(line, "Sched Debug Version: v0.11") {
return nil, fmt.Errorf("unsupported sched_debug version: %v", line)
}
return &schedDebugReaderStateWaitingHeader{}, nil
}
return self, nil
}
// State: WaitingHeader
// In this state the state machine is waiting for the header of the stats table.
// It will transit to ReadingTask state once it received a header.
type schedDebugReaderStateWaitingHeader struct {
}
func (self *schedDebugReaderStateWaitingHeader) isSeparator(line string) bool {
line = strings.TrimSpace(line)
if len(line) == 0 {
return false
}
for _, ch := range line {
if ch != '-' {
return false
}
}
return true
}
func (self *schedDebugReaderStateWaitingHeader) Transit(context *schedDebugContext, line string) (schedDebugReaderState, error) {
if self.isSeparator(line) {
// A new table of runnable tasks
context.numCores++
loadMap := make(map[string][]int, len(context.loadMap))
for container, loads := range context.loadMap {
for len(loads) < context.numCores {
loads = append(loads, 0)
}
loadMap[container] = loads
}
context.loadMap = loadMap
ret := &schedDebugReaderStateReadingTasks{}
return ret, nil
}
return self, nil
}
// State: ReadingTasks
// In this state, the state machine expects that each line contains a stats info of a thread.
// It will transit to WaitingHeader state once it reads an empty line.
type schedDebugReaderStateReadingTasks struct {
}
func (self *schedDebugReaderStateReadingTasks) Transit(context *schedDebugContext, line string) (schedDebugReaderState, error) {
line = strings.TrimSpace(line)
if len(line) == 0 {
// End of a table. Waiting for another table.
ret := &schedDebugReaderStateWaitingHeader{}
return ret, nil
}
fields := strings.Fields(line)
// Example of one row:
// rcuos/2 10 153908711.511953 9697302 120 153908711.511953 462044.084299 4409304806.819551 0 /
container := fields[len(fields)-1]
var loads []int
ok := false
if loads, ok = context.loadMap[container]; !ok {
loads = make([]int, context.numCores)
}
for len(loads) < context.numCores {
loads = append(loads, 0)
}
loads[len(loads)-1]++
context.loadMap[container] = loads
return self, nil
}