github.com/iotexproject/iotex-core@v1.14.1-rc1/pkg/recovery/recovery.go (about) 1 // Copyright (c) 2022 IoTeX 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package recovery 7 8 import ( 9 "os" 10 "path/filepath" 11 "runtime/debug" 12 "runtime/pprof" 13 "time" 14 15 "github.com/shirou/gopsutil/v3/cpu" 16 "github.com/shirou/gopsutil/v3/disk" 17 "github.com/shirou/gopsutil/v3/load" 18 "github.com/shirou/gopsutil/v3/mem" 19 20 "github.com/iotexproject/iotex-core/pkg/log" 21 "github.com/iotexproject/iotex-core/pkg/util/fileutil" 22 ) 23 24 type ( 25 // CPUInfo stat cpu infos 26 CPUInfo struct { 27 PhysicalCount int `json:"physical_count"` 28 LogicalCount int `json:"logical_count"` 29 TotalPercent []float64 `json:"total_use_percent"` 30 PerPercent []float64 `json:"per_use_percent"` 31 Loads *load.AvgStat `json:"average_loads"` 32 Times []cpu.TimesStat `json:"running_times"` 33 Infos []cpu.InfoStat `json:"infos"` 34 } 35 // MemInfo stat memory infos 36 MemInfo struct { 37 Virtual *mem.VirtualMemoryStat `json:"virtaul"` 38 Swap *mem.SwapMemoryStat `json:"swap"` 39 } 40 // DiskInfo stat disk infos 41 DiskInfo struct { 42 IOCounters map[string]disk.IOCountersStat `json:"io_counters"` 43 Partitions []disk.PartitionStat `json:"partitions"` 44 } 45 ) 46 47 // _crashlogDir saves the directory of crashlog 48 var _crashlogDir string = "/" 49 50 // Recover catchs the crashing goroutine 51 func Recover() { 52 if r := recover(); r != nil { 53 LogCrash(r) 54 } 55 } 56 57 // SetCrashlogDir set the directory of crashlog 58 func SetCrashlogDir(dir string) error { 59 if !fileutil.FileExists(dir) { 60 if err := os.MkdirAll(dir, 0744); err != nil { 61 log.S().Error(err.Error()) 62 return err 63 } 64 } 65 _crashlogDir = dir 66 return nil 67 } 68 69 // LogCrash write down the current memory and stack info and the cpu/mem/disk infos into log dir 70 func LogCrash(r interface{}) { 71 log.S().Errorf("crashlog: %v", r) 72 writeHeapProfile(filepath.Join(_crashlogDir, 73 "heapdump_"+time.Now().String()+".out")) 74 log.S().Infow("crashlog", "stack", string(debug.Stack())) 75 printInfo("cpu", cpuInfo) 76 printInfo("memory", memInfo) 77 printInfo("disk", diskInfo) 78 } 79 80 func writeHeapProfile(path string) { 81 f, err := os.OpenFile(filepath.Clean(path), os.O_CREATE|os.O_RDWR, 0600) 82 if err != nil { 83 log.S().Errorf("crashlog: open heap profile error: %v", err) 84 return 85 } 86 defer func() { 87 if err = f.Close(); err != nil { 88 log.S().Errorf("crashlog: close heap profile error: %v", err) 89 return 90 } 91 }() 92 if err := pprof.WriteHeapProfile(f); err != nil { 93 log.S().Errorf("crashlog: write heap profile error: %v", err) 94 return 95 } 96 } 97 98 func printInfo(name string, info func() (interface{}, error)) { 99 v, err := info() 100 if err != nil { 101 log.S().Errorw("crashlog: get %s info occur error: %v", name, err) 102 return 103 } 104 log.S().Infow("crashlog", name, v) 105 } 106 107 func cpuInfo() (interface{}, error) { 108 // cpu cores 109 physicalCnt, err := cpu.Counts(false) 110 if err != nil { 111 return nil, err 112 } 113 logicalCnt, err := cpu.Counts(true) 114 if err != nil { 115 return nil, err 116 } 117 118 // cpu use percent during past 3 seconds 119 totalPercent, err := cpu.Percent(100*time.Millisecond, false) 120 if err != nil { 121 return nil, err 122 } 123 perPercents, err := cpu.Percent(100*time.Millisecond, true) 124 if err != nil { 125 return nil, err 126 } 127 128 // cpu load stats 129 // load1 respect avrage loads of tasks during past 1 minute 130 // load5 respect avrage loads of tasks during past 5 minute 131 // load15 respect avrage loads of tasks during past 15 minute 132 loads, err := load.Avg() 133 if err != nil { 134 return nil, err 135 } 136 137 // cpu run time from startup 138 times, err := cpu.Times(false) 139 if err != nil { 140 return nil, err 141 } 142 pertimes, err := cpu.Times(true) 143 if err != nil { 144 return nil, err 145 } 146 times = append(times, pertimes...) 147 148 // cpu info 149 infos, err := cpu.Info() 150 if err != nil { 151 return nil, err 152 } 153 154 return &CPUInfo{ 155 PhysicalCount: physicalCnt, 156 LogicalCount: logicalCnt, 157 TotalPercent: totalPercent, 158 PerPercent: perPercents, 159 Loads: loads, 160 Times: times, 161 Infos: infos, 162 }, nil 163 } 164 165 func memInfo() (interface{}, error) { 166 virtual, err := mem.VirtualMemory() 167 if err != nil { 168 return nil, err 169 } 170 swap, err := mem.SwapMemory() 171 if err != nil { 172 return nil, err 173 } 174 return &MemInfo{ 175 Virtual: virtual, 176 Swap: swap, 177 }, nil 178 } 179 180 func diskInfo() (interface{}, error) { 181 ioCounters, err := disk.IOCounters() 182 if err != nil { 183 return nil, err 184 } 185 partitions, err := disk.Partitions(false) 186 if err != nil { 187 return nil, err 188 } 189 return &DiskInfo{ 190 IOCounters: ioCounters, 191 Partitions: partitions, 192 }, nil 193 }