| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #ifndef NDEBUG | |
| 6 | |
| 7 #include "debug.h" | |
| 8 | |
| 9 namespace playground { | |
| 10 | |
| 11 bool Debug::enabled_; | |
| 12 int Debug::numSyscallNames_; | |
| 13 const char **Debug::syscallNames_; | |
| 14 std::map<int, std::string> Debug::syscallNamesMap_; | |
| 15 | |
| 16 Debug Debug::debug_; | |
| 17 | |
| 18 Debug::Debug() { | |
| 19 // Logging is disabled by default, but can be turned on by setting an | |
| 20 // appropriate environment variable. Initialize this code from a global | |
| 21 // constructor, so that it runs before the sandbox is turned on. | |
| 22 enabled_ = !!getenv("SECCOMP_SANDBOX_DEBUGGING"); | |
| 23 | |
| 24 // Read names of system calls from header files, if available. Symbolic | |
| 25 // names make debugging so much nicer. | |
| 26 if (enabled_) { | |
| 27 static const char *filenames[] = { | |
| 28 #if __WORDSIZE == 64 | |
| 29 "/usr/include/asm/unistd_64.h", | |
| 30 #elif __WORDSIZE == 32 | |
| 31 "/usr/include/asm/unistd_32.h", | |
| 32 #endif | |
| 33 "/usr/include/asm/unistd.h", | |
| 34 NULL }; | |
| 35 numSyscallNames_ = 0; | |
| 36 for (const char **fn = filenames; *fn; ++fn) { | |
| 37 FILE *fp = fopen(*fn, "r"); | |
| 38 if (fp) { | |
| 39 std::string baseName; | |
| 40 int baseNum = -1; | |
| 41 char buf[80]; | |
| 42 while (fgets(buf, sizeof(buf), fp)) { | |
| 43 // Check if the line starts with "#define" | |
| 44 static const char* whitespace = " \t\r\n"; | |
| 45 char *token, *save; | |
| 46 token = strtok_r(buf, whitespace, &save); | |
| 47 if (token && !strcmp(token, "#define")) { | |
| 48 | |
| 49 // Only parse identifiers that start with "__NR_" | |
| 50 token = strtok_r(NULL, whitespace, &save); | |
| 51 if (token) { | |
| 52 if (strncmp(token, "__NR_", 5)) { | |
| 53 continue; | |
| 54 } | |
| 55 std::string syscallName(token + 5); | |
| 56 | |
| 57 // Parse the value of the symbol. Try to be forgiving in what | |
| 58 // we accept, as the file format might change over time. | |
| 59 token = strtok_r(NULL, "\r\n", &save); | |
| 60 if (token) { | |
| 61 // Some values are defined relative to previous values, we | |
| 62 // detect these examples by finding an earlier symbol name | |
| 63 // followed by a '+' plus character. | |
| 64 bool isRelative = false; | |
| 65 char *base = strstr(token, baseName.c_str()); | |
| 66 if (baseNum >= 0 && base) { | |
| 67 base += baseName.length(); | |
| 68 while (*base == ' ' || *base == '\t') { | |
| 69 ++base; | |
| 70 } | |
| 71 if (*base == '+') { | |
| 72 isRelative = true; | |
| 73 token = base; | |
| 74 } | |
| 75 } | |
| 76 | |
| 77 // Skip any characters that are not part of the syscall number. | |
| 78 while (*token < '0' || *token > '9') { | |
| 79 token++; | |
| 80 } | |
| 81 | |
| 82 // If we now have a valid datum, enter it into our map. | |
| 83 if (*token) { | |
| 84 int sysnum = atoi(token); | |
| 85 | |
| 86 // Deal with symbols that are defined relative to earlier | |
| 87 // ones. | |
| 88 if (isRelative) { | |
| 89 sysnum += baseNum; | |
| 90 } else { | |
| 91 baseNum = sysnum; | |
| 92 baseName = syscallName; | |
| 93 } | |
| 94 | |
| 95 // Keep track of the highest syscall number that we know | |
| 96 // about. | |
| 97 if (sysnum >= numSyscallNames_) { | |
| 98 numSyscallNames_ = sysnum + 1; | |
| 99 } | |
| 100 | |
| 101 syscallNamesMap_[sysnum] = syscallName; | |
| 102 } | |
| 103 } | |
| 104 } | |
| 105 } | |
| 106 } | |
| 107 fclose(fp); | |
| 108 break; | |
| 109 } | |
| 110 } | |
| 111 if (numSyscallNames_) { | |
| 112 // We cannot make system calls at the time, when we are looking up | |
| 113 // the names. So, copy them into a data structure that can be | |
| 114 // accessed without having to allocated memory (i.e. no more STL). | |
| 115 syscallNames_ = reinterpret_cast<const char **>( | |
| 116 calloc(sizeof(char *), numSyscallNames_)); | |
| 117 for (std::map<int, std::string>::const_iterator iter = | |
| 118 syscallNamesMap_.begin(); | |
| 119 iter != syscallNamesMap_.end(); | |
| 120 ++iter) { | |
| 121 syscallNames_[iter->first] = iter->second.c_str(); | |
| 122 } | |
| 123 } | |
| 124 } | |
| 125 } | |
| 126 | |
| 127 bool Debug::enter() { | |
| 128 // Increment the recursion level in TLS storage. This allows us to | |
| 129 // make system calls from within our debugging functions, without triggering | |
| 130 // additional debugging output. | |
| 131 // | |
| 132 // This function can be called from both the sandboxed process and from the | |
| 133 // trusted process. Only the sandboxed process needs to worry about | |
| 134 // recursively calling system calls. The trusted process doesn't intercept | |
| 135 // system calls and thus doesn't have this problem. It also doesn't have | |
| 136 // a TLS. We explicitly set the segment selector to zero, when in the | |
| 137 // trusted process, so that we can avoid tracking recursion levels. | |
| 138 int level; | |
| 139 #if defined(__x86_64__) | |
| 140 asm volatile("mov %%gs, %0\n" | |
| 141 "test %0, %0\n" | |
| 142 "jz 1f\n" | |
| 143 "movl %%gs:0x1050-0xE0, %0\n" | |
| 144 "incl %%gs:0x1050-0xE0\n" | |
| 145 "1:\n" | |
| 146 : "=r"(level) | |
| 147 : | |
| 148 : "memory"); | |
| 149 #elif defined(__i386__) | |
| 150 asm volatile("mov %%fs, %0\n" | |
| 151 "test %0, %0\n" | |
| 152 "jz 1f\n" | |
| 153 "movl %%fs:0x1034-0x58, %0\n" | |
| 154 "incl %%fs:0x1034-0x58\n" | |
| 155 "1:\n" | |
| 156 : "=r"(level) | |
| 157 : | |
| 158 : "memory"); | |
| 159 #else | |
| 160 #error "Unsupported target platform" | |
| 161 #endif | |
| 162 return !level; | |
| 163 } | |
| 164 | |
| 165 bool Debug::leave() { | |
| 166 // Decrement the recursion level in TLS storage. This allows us to | |
| 167 // make system calls from within our debugging functions, without triggering | |
| 168 // additional debugging output. | |
| 169 // | |
| 170 // This function can be called from both the sandboxed process and from the | |
| 171 // trusted process. Only the sandboxed process needs to worry about | |
| 172 // recursively calling system calls. The trusted process doesn't intercept | |
| 173 // system calls and thus doesn't have this problem. It also doesn't have | |
| 174 // a TLS. We explicitly set the segment selector to zero, when in the | |
| 175 // trusted process, so that we can avoid tracking recursion levels. | |
| 176 int level; | |
| 177 #if defined(__x86_64__) | |
| 178 asm volatile("mov %%gs, %0\n" | |
| 179 "test %0, %0\n" | |
| 180 "jz 1f\n" | |
| 181 "decl %%gs:0x1050-0xE0\n" | |
| 182 "movl %%gs:0x1050-0xE0, %0\n" | |
| 183 "1:\n" | |
| 184 : "=r"(level) | |
| 185 : | |
| 186 : "memory"); | |
| 187 #elif defined(__i386__) | |
| 188 asm volatile("mov %%fs, %0\n" | |
| 189 "test %0, %0\n" | |
| 190 "jz 1f\n" | |
| 191 "decl %%fs:0x1034-0x58\n" | |
| 192 "movl %%fs:0x1034-0x58, %0\n" | |
| 193 "1:\n" | |
| 194 : "=r"(level) | |
| 195 : | |
| 196 : "memory"); | |
| 197 #else | |
| 198 #error Unsupported target platform | |
| 199 #endif | |
| 200 return !level; | |
| 201 } | |
| 202 | |
| 203 void Debug::_message(const char* msg) { | |
| 204 if (enabled_) { | |
| 205 Sandbox::SysCalls sys; | |
| 206 size_t len = strlen(msg); | |
| 207 if (len && msg[len-1] != '\n') { | |
| 208 // Write operations should be atomic, so that we don't interleave | |
| 209 // messages from multiple threads. Append a newline, if it is not | |
| 210 // already there. | |
| 211 char copy[len + 1]; | |
| 212 memcpy(copy, msg, len); | |
| 213 copy[len] = '\n'; | |
| 214 Sandbox::write(sys, 2, copy, len + 1); | |
| 215 } else { | |
| 216 Sandbox::write(sys, 2, msg, len); | |
| 217 } | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 void Debug::message(const char* msg) { | |
| 222 if (enabled_) { | |
| 223 if (enter()) { | |
| 224 _message(msg); | |
| 225 } | |
| 226 leave(); | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 void Debug::gettimeofday(long long* tm) { | |
| 231 if (tm) { | |
| 232 struct timeval tv; | |
| 233 #if defined(__i386__) | |
| 234 // Zero out the lastSyscallNum, so that we don't try to coalesce | |
| 235 // calls to gettimeofday(). For debugging purposes, we need the | |
| 236 // exact time. | |
| 237 asm volatile("movl $0, %fs:0x102C-0x58"); | |
| 238 #elif !defined(__x86_64__) | |
| 239 #error Unsupported target platform | |
| 240 #endif | |
| 241 ::gettimeofday(&tv, NULL); | |
| 242 *tm = 1000ULL*1000ULL*static_cast<unsigned>(tv.tv_sec) + | |
| 243 static_cast<unsigned>(tv.tv_usec); | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 void Debug::syscall(long long* tm, int sysnum, const char* msg, int call) { | |
| 248 // This function gets called from the system call wrapper. Avoid calling | |
| 249 // any library functions that themselves need system calls. | |
| 250 if (enabled_) { | |
| 251 if (enter() || !tm) { | |
| 252 gettimeofday(tm); | |
| 253 | |
| 254 const char *sysname = NULL; | |
| 255 if (sysnum >= 0 && sysnum < numSyscallNames_) { | |
| 256 sysname = syscallNames_[sysnum]; | |
| 257 } | |
| 258 static const char kUnnamedMessage[] = "Unnamed syscall #"; | |
| 259 char unnamed[40]; | |
| 260 if (!sysname) { | |
| 261 memcpy(unnamed, kUnnamedMessage, sizeof(kUnnamedMessage) - 1); | |
| 262 itoa(unnamed + sizeof(kUnnamedMessage) - 1, sysnum); | |
| 263 sysname = unnamed; | |
| 264 } | |
| 265 #if defined(__NR_socketcall) || defined(__NR_ipc) | |
| 266 char extra[40]; | |
| 267 *extra = '\000'; | |
| 268 #if defined(__NR_socketcall) | |
| 269 if (sysnum == __NR_socketcall) { | |
| 270 static const char* socketcall_name[] = { | |
| 271 0, "socket", "bind", "connect", "listen", "accept", "getsockname", | |
| 272 "getpeername", "socketpair", "send", "recv", "sendto","recvfrom", | |
| 273 "shutdown", "setsockopt", "getsockopt", "sendmsg", "recvmsg", | |
| 274 "accept4" | |
| 275 }; | |
| 276 if (call >= 1 && | |
| 277 call < (int)(sizeof(socketcall_name)/sizeof(char *))) { | |
| 278 strcat(strcpy(extra, " "), socketcall_name[call]); | |
| 279 } else { | |
| 280 itoa(strcpy(extra, " #") + 2, call); | |
| 281 } | |
| 282 } | |
| 283 #endif | |
| 284 #if defined(__NR_ipc) | |
| 285 if (sysnum == __NR_ipc) { | |
| 286 static const char* ipc_name[] = { | |
| 287 0, "semop", "semget", "semctl", "semtimedop", 0, 0, 0, 0, 0, 0, | |
| 288 "msgsnd", "msgrcv", "msgget", "msgctl", 0, 0, 0, 0, 0, 0, | |
| 289 "shmat", "shmdt", "shmget", "shmctl" }; | |
| 290 if (call >= 1 && call < (int)(sizeof(ipc_name)/sizeof(char *)) && | |
| 291 ipc_name[call]) { | |
| 292 strcat(strcpy(extra, " "), ipc_name[call]); | |
| 293 } else { | |
| 294 itoa(strcpy(extra, " #") + 2, call); | |
| 295 } | |
| 296 } | |
| 297 #endif | |
| 298 #else | |
| 299 static const char extra[1] = { 0 }; | |
| 300 #endif | |
| 301 char buf[strlen(sysname) + strlen(extra) + (msg ? strlen(msg) : 0) + 4]; | |
| 302 strcat(strcat(strcat(strcat(strcpy(buf, sysname), extra), ": "), | |
| 303 msg ? msg : ""), "\n"); | |
| 304 _message(buf); | |
| 305 } | |
| 306 leave(); | |
| 307 } | |
| 308 } | |
| 309 | |
| 310 char* Debug::itoa(char* s, int n) { | |
| 311 // Remember return value | |
| 312 char *ret = s; | |
| 313 | |
| 314 // Insert sign for negative numbers | |
| 315 if (n < 0) { | |
| 316 *s++ = '-'; | |
| 317 n = -n; | |
| 318 } | |
| 319 | |
| 320 // Convert to decimal (in reverse order) | |
| 321 char *start = s; | |
| 322 do { | |
| 323 *s++ = '0' + (n % 10); | |
| 324 n /= 10; | |
| 325 } while (n); | |
| 326 *s-- = '\000'; | |
| 327 | |
| 328 // Reverse order of digits | |
| 329 while (start < s) { | |
| 330 char ch = *s; | |
| 331 *s-- = *start; | |
| 332 *start++ = ch; | |
| 333 } | |
| 334 | |
| 335 return ret; | |
| 336 } | |
| 337 | |
| 338 void Debug::elapsed(long long tm, int sysnum, int call) { | |
| 339 if (enabled_) { | |
| 340 if (enter()) { | |
| 341 // Compute the time that has passed since the system call started. | |
| 342 long long delta; | |
| 343 gettimeofday(&delta); | |
| 344 delta -= tm; | |
| 345 | |
| 346 // Format "Elapsed time: %d.%03dms" without using sprintf(). | |
| 347 char buf[80]; | |
| 348 itoa(strrchr(strcpy(buf, "Elapsed time: "), '\000'), delta/1000); | |
| 349 delta %= 1000; | |
| 350 strcat(buf, delta < 100 ? delta < 10 ? ".00" : ".0" : "."); | |
| 351 itoa(strrchr(buf, '\000'), delta); | |
| 352 strcat(buf, "ms"); | |
| 353 | |
| 354 // Print system call name and elapsed time. | |
| 355 syscall(NULL, sysnum, buf, call); | |
| 356 } | |
| 357 leave(); | |
| 358 } | |
| 359 } | |
| 360 | |
| 361 } // namespace | |
| 362 | |
| 363 #endif // NDEBUG | |
| OLD | NEW |