154 lines
3.7 KiB
Go
154 lines
3.7 KiB
Go
//go:build windows
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
func cleanLogText(s string, limit int) string {
|
|
var out []rune
|
|
for _, r := range s {
|
|
if unicode.IsControl(r) || unicode.In(r, unicode.Cf) || r == '|' {
|
|
r = ' '
|
|
}
|
|
if len(out) >= limit {
|
|
if limit >= 3 {
|
|
out = append(out[:limit-3], '.', '.', '.')
|
|
}
|
|
break
|
|
}
|
|
out = append(out, r)
|
|
}
|
|
return string(out)
|
|
}
|
|
func priorityRank(p uint32) int {
|
|
switch p {
|
|
case PriorityClassIdle:
|
|
return 0
|
|
case PriorityClassBelowNormal:
|
|
return 1
|
|
case PriorityClassNormal:
|
|
return 2
|
|
case PriorityClassAboveNormal:
|
|
return 3
|
|
case PriorityClassHigh:
|
|
return 4
|
|
case PriorityClassRealtime:
|
|
return 5
|
|
}
|
|
return -1
|
|
}
|
|
func logMemory(b uint64) string {
|
|
if b >= 1<<30 {
|
|
return fmt.Sprintf("%.1f ГБ", float64(b)/(1<<30))
|
|
}
|
|
if b >= 1<<20 {
|
|
return fmt.Sprintf("%d МБ", b>>20)
|
|
}
|
|
if b >= 1<<10 {
|
|
return fmt.Sprintf("%d КБ", b>>10)
|
|
}
|
|
return fmt.Sprintf("%d Б", b)
|
|
}
|
|
func writeLogTable(b *strings.Builder, cells [][]string) {
|
|
widths := make([]int, len(cells[0]))
|
|
for _, row := range cells {
|
|
for i, value := range row {
|
|
widths[i] = max(widths[i], utf8.RuneCountInString(value))
|
|
}
|
|
}
|
|
var border strings.Builder
|
|
border.WriteByte('+')
|
|
for _, width := range widths {
|
|
border.WriteString(strings.Repeat("-", width+2))
|
|
border.WriteByte('+')
|
|
}
|
|
border.WriteByte('\n')
|
|
b.WriteString(border.String())
|
|
for n, row := range cells {
|
|
b.WriteByte('|')
|
|
for i, value := range row {
|
|
fmt.Fprintf(b, " %-*s |", widths[i], value)
|
|
}
|
|
b.WriteByte('\n')
|
|
if n == 0 {
|
|
b.WriteString(border.String())
|
|
}
|
|
}
|
|
b.WriteString(border.String())
|
|
}
|
|
func formatReport(r scanReport, when time.Time) string {
|
|
if r.Title == "" && len(r.Rows) == 0 && len(r.Errors) == 0 {
|
|
return ""
|
|
}
|
|
var b strings.Builder
|
|
header := func(title string) {
|
|
fmt.Fprintf(&b, "[%s] %s", when.Format("02.01.2006 15:04:05"), cleanLogText(title, 200))
|
|
if r.DryRun {
|
|
b.WriteString(" [DRY-RUN: ПЛАН, БЕЗ ИЗМЕНЕНИЙ]")
|
|
}
|
|
b.WriteByte('\n')
|
|
}
|
|
if r.Title != "" || len(r.Rows) > 0 {
|
|
title := r.Title
|
|
if title == "" {
|
|
title = "ИЗМЕНЕНИЕ ПРИОРИТЕТОВ"
|
|
}
|
|
header(title)
|
|
if len(r.Rows) > 0 {
|
|
rows := append([]priorityChange(nil), r.Rows...)
|
|
sort.SliceStable(rows, func(i, j int) bool {
|
|
if rows[i].Heavy != rows[j].Heavy {
|
|
return rows[i].Heavy
|
|
}
|
|
return rows[i].PID < rows[j].PID
|
|
})
|
|
cells := [][]string{{"Процесс", "PID", "Память", "Повышено", "Понижено"}}
|
|
for _, row := range rows {
|
|
columns := []string{cleanLogText(row.Name, 64), strconv.FormatUint(uint64(row.PID), 10), logMemory(row.RSS), "", ""}
|
|
if row.Blocked {
|
|
columns[3] = "в блоклисте"
|
|
} else if row.Unchanged {
|
|
columns[3] = "уже " + prioName(row.After)
|
|
} else {
|
|
change := prioName(row.Before) + " -> " + prioName(row.After)
|
|
if priorityRank(row.After) > priorityRank(row.Before) {
|
|
columns[3] = change
|
|
} else {
|
|
columns[4] = change
|
|
}
|
|
}
|
|
cells = append(cells, columns)
|
|
}
|
|
writeLogTable(&b, cells)
|
|
}
|
|
b.WriteByte('\n')
|
|
}
|
|
if len(r.Errors) > 0 {
|
|
header("ОШИБКИ")
|
|
rows := append([]processError(nil), r.Errors...)
|
|
sort.SliceStable(rows, func(i, j int) bool { return rows[i].PID < rows[j].PID })
|
|
cells := [][]string{{"Процесс", "PID", "Описание ошибки"}}
|
|
for _, problem := range rows {
|
|
name, pid := problem.Name, "-"
|
|
if name == "" {
|
|
name = "autoPriority"
|
|
}
|
|
if problem.PID != 0 {
|
|
pid = strconv.FormatUint(uint64(problem.PID), 10)
|
|
}
|
|
cells = append(cells, []string{cleanLogText(name, 64), pid, cleanLogText(problem.Description, 1500)})
|
|
}
|
|
writeLogTable(&b, cells)
|
|
b.WriteByte('\n')
|
|
}
|
|
return b.String()
|
|
}
|