github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/kernel/task_log.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package kernel 16 17 import ( 18 "fmt" 19 "runtime/trace" 20 "sort" 21 22 "github.com/SagerNet/gvisor/pkg/context" 23 "github.com/SagerNet/gvisor/pkg/hostarch" 24 "github.com/SagerNet/gvisor/pkg/log" 25 "github.com/SagerNet/gvisor/pkg/usermem" 26 ) 27 28 const ( 29 // maxStackDebugBytes is the maximum number of user stack bytes that may be 30 // printed by debugDumpStack. 31 maxStackDebugBytes = 1024 32 // maxCodeDebugBytes is the maximum number of user code bytes that may be 33 // printed by debugDumpCode. 34 maxCodeDebugBytes = 128 35 ) 36 37 // Infof logs an formatted info message by calling log.Infof. 38 func (t *Task) Infof(fmt string, v ...interface{}) { 39 if log.IsLogging(log.Info) { 40 log.InfofAtDepth(1, t.logPrefix.Load().(string)+fmt, v...) 41 } 42 } 43 44 // Warningf logs a warning string by calling log.Warningf. 45 func (t *Task) Warningf(fmt string, v ...interface{}) { 46 if log.IsLogging(log.Warning) { 47 log.WarningfAtDepth(1, t.logPrefix.Load().(string)+fmt, v...) 48 } 49 } 50 51 // Debugf creates a debug string that includes the task ID. 52 func (t *Task) Debugf(fmt string, v ...interface{}) { 53 if log.IsLogging(log.Debug) { 54 log.DebugfAtDepth(1, t.logPrefix.Load().(string)+fmt, v...) 55 } 56 } 57 58 // IsLogging returns true iff this level is being logged. 59 func (t *Task) IsLogging(level log.Level) bool { 60 return log.IsLogging(level) 61 } 62 63 // DebugDumpState logs task state at log level debug. 64 // 65 // Preconditions: The caller must be running on the task goroutine. 66 func (t *Task) DebugDumpState() { 67 t.debugDumpRegisters() 68 t.debugDumpStack() 69 t.debugDumpCode() 70 if mm := t.MemoryManager(); mm != nil { 71 t.Debugf("Mappings:\n%s", mm) 72 } 73 t.Debugf("FDTable:\n%s", t.fdTable) 74 } 75 76 // debugDumpRegisters logs register state at log level debug. 77 // 78 // Preconditions: The caller must be running on the task goroutine. 79 func (t *Task) debugDumpRegisters() { 80 if !t.IsLogging(log.Debug) { 81 return 82 } 83 regmap, err := t.Arch().RegisterMap() 84 if err != nil { 85 t.Debugf("Registers: %v", err) 86 } else { 87 t.Debugf("Registers:") 88 var regs []string 89 for reg := range regmap { 90 regs = append(regs, reg) 91 } 92 sort.Strings(regs) 93 for _, reg := range regs { 94 t.Debugf("%-8s = %016x", reg, regmap[reg]) 95 } 96 } 97 } 98 99 // debugDumpStack logs user stack contents at log level debug. 100 // 101 // Preconditions: The caller must be running on the task goroutine. 102 func (t *Task) debugDumpStack() { 103 if !t.IsLogging(log.Debug) { 104 return 105 } 106 m := t.MemoryManager() 107 if m == nil { 108 t.Debugf("Memory manager for task is gone, skipping application stack dump.") 109 return 110 } 111 t.Debugf("Stack:") 112 start := hostarch.Addr(t.Arch().Stack()) 113 // Round addr down to a 16-byte boundary. 114 start &= ^hostarch.Addr(15) 115 // Print 16 bytes per line, one byte at a time. 116 for offset := uint64(0); offset < maxStackDebugBytes; offset += 16 { 117 addr, ok := start.AddLength(offset) 118 if !ok { 119 break 120 } 121 var data [16]byte 122 n, err := m.CopyIn(t, addr, data[:], usermem.IOOpts{ 123 IgnorePermissions: true, 124 }) 125 // Print as much of the line as we can, even if an error was 126 // encountered. 127 if n > 0 { 128 t.Debugf("%x: % x", addr, data[:n]) 129 } 130 if err != nil { 131 t.Debugf("Error reading stack at address %x: %v", addr+hostarch.Addr(n), err) 132 break 133 } 134 } 135 } 136 137 // debugDumpCode logs user code contents at log level debug. 138 // 139 // Preconditions: The caller must be running on the task goroutine. 140 func (t *Task) debugDumpCode() { 141 if !t.IsLogging(log.Debug) { 142 return 143 } 144 m := t.MemoryManager() 145 if m == nil { 146 t.Debugf("Memory manager for task is gone, skipping application code dump.") 147 return 148 } 149 t.Debugf("Code:") 150 // Print code on both sides of the instruction register. 151 start := hostarch.Addr(t.Arch().IP()) - maxCodeDebugBytes/2 152 // Round addr down to a 16-byte boundary. 153 start &= ^hostarch.Addr(15) 154 // Print 16 bytes per line, one byte at a time. 155 for offset := uint64(0); offset < maxCodeDebugBytes; offset += 16 { 156 addr, ok := start.AddLength(offset) 157 if !ok { 158 break 159 } 160 var data [16]byte 161 n, err := m.CopyIn(t, addr, data[:], usermem.IOOpts{ 162 IgnorePermissions: true, 163 }) 164 // Print as much of the line as we can, even if an error was 165 // encountered. 166 if n > 0 { 167 t.Debugf("%x: % x", addr, data[:n]) 168 } 169 if err != nil { 170 t.Debugf("Error reading stack at address %x: %v", addr+hostarch.Addr(n), err) 171 break 172 } 173 } 174 } 175 176 // trace definitions. 177 // 178 // Note that all region names are prefixed by ':' in order to ensure that they 179 // are lexically ordered before all system calls, which use the naked system 180 // call name (e.g. "read") for maximum clarity. 181 const ( 182 traceCategory = "task" 183 runRegion = ":run" 184 blockRegion = ":block" 185 cpuidRegion = ":cpuid" 186 faultRegion = ":fault" 187 ) 188 189 // updateInfoLocked updates the task's cached log prefix and tracing 190 // information to reflect its current thread ID. 191 // 192 // Preconditions: The task's owning TaskSet.mu must be locked. 193 func (t *Task) updateInfoLocked() { 194 // Use the task's TID in the root PID namespace for logging. 195 tid := t.tg.pidns.owner.Root.tids[t] 196 t.logPrefix.Store(fmt.Sprintf("[% 4d] ", tid)) 197 t.rebuildTraceContext(tid) 198 } 199 200 // rebuildTraceContext rebuilds the trace context. 201 // 202 // Precondition: the passed tid must be the tid in the root namespace. 203 func (t *Task) rebuildTraceContext(tid ThreadID) { 204 // Re-initialize the trace context. 205 if t.traceTask != nil { 206 t.traceTask.End() 207 } 208 209 // Note that we define the "task type" to be the dynamic TID. This does 210 // not align perfectly with the documentation for "tasks" in the 211 // tracing package. Tasks may be assumed to be bounded by analysis 212 // tools. However, if we just use a generic "task" type here, then the 213 // "user-defined tasks" page on the tracing dashboard becomes nearly 214 // unusable, as it loads all traces from all tasks. 215 // 216 // We can assume that the number of tasks in the system is not 217 // arbitrarily large (in general it won't be, especially for cases 218 // where we're collecting a brief profile), so using the TID is a 219 // reasonable compromise in this case. 220 t.traceContext, t.traceTask = trace.NewTask(context.Background(), fmt.Sprintf("tid:%d", tid)) 221 } 222 223 // traceCloneEvent is called when a new task is spawned. 224 // 225 // ntid must be the new task's ThreadID in the root namespace. 226 func (t *Task) traceCloneEvent(ntid ThreadID) { 227 if !trace.IsEnabled() { 228 return 229 } 230 trace.Logf(t.traceContext, traceCategory, "spawn: %d", ntid) 231 } 232 233 // traceExitEvent is called when a task exits. 234 func (t *Task) traceExitEvent() { 235 if !trace.IsEnabled() { 236 return 237 } 238 trace.Logf(t.traceContext, traceCategory, "exit status: 0x%x", t.exitStatus.Status()) 239 } 240 241 // traceExecEvent is called when a task calls exec. 242 func (t *Task) traceExecEvent(image *TaskImage) { 243 if !trace.IsEnabled() { 244 return 245 } 246 file := image.MemoryManager.Executable() 247 if file == nil { 248 trace.Logf(t.traceContext, traceCategory, "exec: << unknown >>") 249 return 250 } 251 defer file.DecRef(t) 252 trace.Logf(t.traceContext, traceCategory, "exec: %s", file.PathnameWithDeleted(t)) 253 }