blob: 393219cce0571242d52d01a5db2ead27a63ed9d8 [file]
// Copyright (c) 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package android
import (
"context"
"os/user"
"path/filepath"
"regexp"
"go.chromium.org/luci/common/logging"
"go.chromium.org/luci/common/tsmon"
"go.chromium.org/luci/common/tsmon/field"
"go.chromium.org/luci/common/tsmon/metric"
"go.chromium.org/luci/common/tsmon/types"
)
var (
cpuTemp = metric.NewFloat("dev/mobile/cpu/temperature",
"device CPU temperature in deg C",
&types.MetricMetadata{Units: types.DegreeCelsiusUnit},
field.String("device_id"))
battTemp = metric.NewFloat("dev/mobile/battery/temperature",
"battery temperature in deg C",
&types.MetricMetadata{Units: types.DegreeCelsiusUnit},
field.String("device_id"))
battCharge = metric.NewFloat("dev/mobile/battery/charge",
"percentage charge of battery",
nil,
field.String("device_id"))
devStatus = metric.NewString("dev/mobile/status",
"operational state of device",
nil,
field.String("device_id"),
field.String("imei"))
devType = metric.NewString("dev/mobile/type",
"device hardware or type",
nil,
field.String("device_id"))
devOS = metric.NewString("dev/mobile/os",
"operating system of the device",
nil,
field.String("device_id"))
devUptime = metric.NewFloat("dev/mobile/uptime",
"device uptime in seconds",
&types.MetricMetadata{Units: types.Seconds},
field.String("device_id"))
memFree = metric.NewInt("dev/mobile/mem/free",
"available memory (free + cached + buffers) in kb",
&types.MetricMetadata{Units: types.Kibibytes},
field.String("device_id"))
memTotal = metric.NewInt("dev/mobile/mem/total",
"total memory (device ram - kernel leaks) in kb",
&types.MetricMetadata{Units: types.Kibibytes},
field.String("device_id"))
procCount = metric.NewInt("dev/mobile/proc/count",
"process count",
nil,
field.String("device_id"))
metricReadStatus = metric.NewString("dev/android_device_metric_read/status",
"status of the last metric read",
nil,
field.String("file_name"))
metricSecondsStale = metric.NewFloat("dev/android_device_metric_read/seconds_stale",
"seconds since the status file was written",
nil,
field.String("file_name"))
portPathRE = regexp.MustCompile(`\d+/\d+`)
allMetrics = []types.Metric{
cpuTemp,
battTemp,
battCharge,
devStatus,
devType,
devOS,
devUptime,
memFree,
memTotal,
procCount,
metricReadStatus,
metricSecondsStale,
}
)
// Register adds tsmon callbacks to set android metrics.
func Register() {
tsmon.RegisterGlobalCallback(func(c context.Context) {
usr, err := user.Current()
if err != nil {
logging.Errorf(c, "Failed to fetch current user: %s", err)
} else if err = update(c, usr.HomeDir); err != nil {
logging.Errorf(c, "Failed to update Android metrics: %s", err)
}
}, allMetrics...)
}
func update(c context.Context, usrHome string) error {
allFiles, err := filepath.Glob(filepath.Join(usrHome, ".android", fileGlob))
if err != nil {
return err
}
if len(allFiles) == 0 {
// Don't log an error message if no files were found - this is the
// usual case on most machines.
return nil
}
var lastErr error
for _, pathToFile := range allFiles {
file, status, staleness, err := loadFile(c, pathToFile)
baseFileName := filepath.Base(pathToFile)
metricReadStatus.Set(c, string(status), baseFileName)
if status == notFound {
logging.Warningf(c, "Expected status file %s, but did not find it.", pathToFile)
continue
}
metricSecondsStale.Set(c, staleness, baseFileName)
if err == nil {
updateFromFile(c, file)
} else {
lastErr = err
}
}
return lastErr
}
func updateFromFile(c context.Context, f deviceStatusFile) {
for name, d := range f.Devices {
if portPathRE.FindStringIndex(name) != nil {
logging.Warningf(c, "Found port path '%s' as device ID, skipping", name)
continue
}
cpuTempValue := d.Temp.GetTemperature()
if cpuTempValue != nil {
cpuTemp.Set(c, *cpuTempValue, name)
}
battTemp.Set(c, d.Battery.GetTemperature(), name)
battCharge.Set(c, d.Battery.Level, name)
if d.Build.ID != "" {
devOS.Set(c, d.Build.ID, name)
}
devStatusValue := d.GetStatus()
if devStatusValue != "" {
devStatus.Set(c, devStatusValue, name, d.IMEI)
}
devTypeValue := d.Build.GetName()
if devTypeValue != "" {
devType.Set(c, devTypeValue, name)
}
devUptime.Set(c, d.Uptime, name)
memFree.Set(c, d.Mem.Avail, name)
memTotal.Set(c, d.Mem.Total, name)
procCount.Set(c, d.Processes, name)
}
}