-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsyslog
executable file
·181 lines (157 loc) · 6.91 KB
/
syslog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#!/usr/bin/env bash
# Copyright © [2023] Mathieu Laparie <mlaparie [at] disr [dot] it>
# MIT License
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# Function to check for required dependencies
check_dependencies() {
local missing=()
local packages=()
# Check for required commands and map to packages
for cmd in cpufreq-info sensors mpstat column top ps; do
if ! type "$cmd" >/dev/null 2>&1; then
missing+=("$cmd")
case "$cmd" in
cpufreq-info) packages+=("cpufrequtils") ;;
sensors) packages+=("lm-sensors") ;;
mpstat) packages+=("sysstat") ;;
column) packages+=("bsdextrautils") ;;
top|ps) packages+=("procps") ;;
esac
fi
done
if [[ ${#missing[@]} -gt 0 ]]; then
printf '\033[31mError: missing dependencies: %s\033[0m\n' "${packages[*]}"
exit 1
fi
}
# Function to show usage/help
usage() {
printf "syslog allows monitoring battery level, power draw, temperature, CPU use and CPU clock over time.
Usage: $0 [-f FILE] [-i INTERVAL]
Options:
-f FILE file to log data to (default: ./sys-log.csv)
-i INTERVAL log clock, set as a duration (default: 60s)
-b BATTERY battery name as referred to in /sys/class/power_supply/ (default: BAT0)
-p plot data to HTML file (same basename as the log file) while logging (may cause CPU spikes)
-h show this help
All arguments are optional. Values can be provided as positional arguments in that order
if not prefixed with flags, or provided in any order if prefixed with flags.
"
exit 0
}
# Plot function
plot() {
local file="${1}"
if [[ -n $plot ]]; then
if ! type python3 >/dev/null 2>&1; then
printf '\033[31mError: missing dependency for plotting data: python3\033[0m\n'
exit 1
else
./plot-syslog.py -i "${file}"
fi
fi
}
# Create file, check variable values and loop
main() {
if ! [[ -d "/sys/class/power_supply/$batname" ]]; then
printf '\033[31mInvalid battery name. Try the following: %s.\033[0m\n' "$(find /sys/class/power_supply -mindepth 1 | cut -d '/' -f 5 | xargs | sed 's/ /, /')"
exit 0
else
# Check if $batname temperature exists, else warn about replacement sensor
temperature=$(sensors | grep -A2 "$batname" | grep -m1 "temp1" | cut -d '+' -f 2 | sed 's/°C//' | cut -d ' ' -f 1)
[ -z "$temperature" ] && printf '\033[33mNo temperature sensor found for %s, using CPU sensor instead.\033[0m\n' "$batname"
# Inform if plotting is enabeld and output file
if [[ -z $plot ]]; then
printf "Logging to '%s' %s at a %s interval…\n\n" "$file" "$interval"
else
printf "Logging to '%s' and plotting to '$(echo "${file}" | cut -d '.' -f -1).html' at a %s interval…\n\n" "$file" "$interval"
fi
# Create the file if it doesn't exist and add header if it's a new file
if [ ! -f "$file" ]; then
echo "Time;Battery (%);Power draw (W);CPU use (%);CPU clock;Temperature (°C);Top-5 processes" > "$file"
fi
# Monitor variables
while :; do
capacity=$(cat "/sys/class/power_supply/$batname/capacity")
draw=$(sensors | grep -Po '[\d.]+\s*(?=W|mW)' | head -1)
unit=$(sensors | grep -Po '[\d.]+\s*(W|mW)' | head -1 | grep -Po '(W|mW)')
[[ "$unit" == "mW" ]] && draw=$(awk "BEGIN { print $draw / 1000 }")
draw=${draw:-NA}
temperature=$(sensors | grep -A2 "$batname" | grep -m1 "temp1" | cut -d '+' -f 2 | sed 's/°C//' | cut -d ' ' -f 1)
[ -z "$temperature" ] && temperature=$(sensors | grep -Po '\+?\K[\d.]+(?=°C)' | head -1)
temperature=${temperature:-NA}
if ! type batman-helper >/dev/null 2>&1; then
# Uncomment if below command doesn't work on smartphone
# percentage=$(top -bn1 | grep "Cpu(s)" | \
# sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | \
# awk '{print 100 - $1"%"}')
percentage=$(mpstat 1 1 | awk '/Average:/ {printf "%.1f%%\n", 100-$NF}' | sed 's/%//')
else
percentage=$(batman-helper cpu)
fi
speed=$(cpufreq-info -m -c $(cpufreq-info -r) | grep "current CPU frequency is" | cut -d ' ' -f 7-8 | cut -d 'H' -f 1-2 | sed 's/Hz./Hz/g')
processes=$(ps -Ao comm --sort=-pcpu | head -n 20 | xargs \
| sed 's/ps //g' \
| sed 's/sed //g' \
| sed 's/head //g' \
| sed 's/xargs //g' \
| sed 's/cut //g' \
| sed 's/bash //g' \
| cut -d ' ' -f 2-6 \
| sed 's/ /, /g')
echo "$(date -Is);$capacity;$draw;$percentage;$speed;$temperature;$processes" >> "$file"
if [[ -z $headersshown ]]; then
head -n 2 "$file" | column -t -s ';' -o ' | ' -R 1,2,3,4,5,6,7 | head -n 1
local headersshown=true
fi
cat "$file" | column -t -s ';' -o ' | ' -R 1,2,3,4,5,6,7 | tail -n 1
# Sleep $interval minus the duration of the mpstat measurement (1s), while allowing intervals
# in multiple units
sleep $(awk 'BEGIN{n=ARGV[1]; u=substr(n,length(n),1); v=substr(n,1,length(n)-1); if(u ~ /[smhd]/){s=(u=="s"?1:u=="m"?60:u=="h"?3600:86400)*v}else{s=n}; print (s-1 > 0 ? s-1 : 0)}' "$interval")
plot "${file}" >/dev/null 2>&1
done
fi
}
# Parse arguments using getopts
file="sys-log.csv"
batname="BAT0"
interval=60s
no_flags=true # Flag to track if no flags were used
while getopts "f:i:b:ph" opt; do
case $opt in
f) file=$OPTARG; no_flags=false ;;
i) interval=$OPTARG; no_flags=false ;;
b) batname=$OPTARG; no_flags=false ;;
p) plot=true; no_flags=false ;;
h | *) usage ;;
esac
done
# Check if no flags were used, only values
if $no_flags && [[ $# -gt 0 ]]; then # Positional arguments detected without flags
printf 'No flag(s) detected, using values as positional arguments instead.\n'
file=${1:-sys-log.csv}
interval=${2:-60s}
batname=${3:-BAT0}
elif [[ $# -gt 0 ]]; then
printf 'Flag(s) detected, any positional argument not prefixed with a flag will be ignored.\n'
else
printf 'No argument(s) provided, using only default values.\n'
fi
# Check for required dependencies before proceeding
check_dependencies
# Main execution
main