1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "SystemThread" 18 19 /* 20 * System thread support. 21 */ 22 #include "Dalvik.h" 23 #include "native/SystemThread.h" 24 25 #include <unistd.h> 26 #include <sys/types.h> 27 #include <sys/stat.h> 28 #include <fcntl.h> 29 #include <errno.h> 30 31 struct SystemThread { 32 /* 33 * /proc/PID/task/TID/stat. -1 if not opened yet. -2 indicates an error 34 * occurred while opening the file. 35 */ 36 int statFile; 37 38 /* Offset of state char in stat file, last we checked. */ 39 int stateOffset; 40 }; 41 42 void dvmDetachSystemThread(Thread* thread) { 43 if (thread->systemThread != NULL) { 44 if (thread->systemThread->statFile > -1) { 45 close(thread->systemThread->statFile); 46 } 47 free(thread->systemThread); 48 thread->systemThread = NULL; 49 } 50 } 51 52 /* Converts a Linux thread state to a ThreadStatus. */ 53 static ThreadStatus stateToStatus(char state) { 54 switch (state) { 55 case 'R': return THREAD_RUNNING; // running 56 case 'S': return THREAD_WAIT; // sleeping in interruptible wait 57 case 'D': return THREAD_WAIT; // uninterruptible disk sleep 58 case 'Z': return THREAD_ZOMBIE; // zombie 59 case 'T': return THREAD_WAIT; // traced or stopped on a signal 60 case 'W': return THREAD_WAIT; // paging memory 61 default: 62 LOGE("Unexpected state: %c", state); 63 return THREAD_NATIVE; 64 } 65 } 66 67 /* Reads the state char starting from the beginning of the file. */ 68 static char readStateFromBeginning(SystemThread* thread) { 69 char buffer[256]; 70 int size = read(thread->statFile, buffer, sizeof(buffer) - 1); 71 if (size <= 0) { 72 LOGE("read() returned %d: %s", size, strerror(errno)); 73 return 0; 74 } 75 char* endOfName = (char*) memchr(buffer, ')', size); 76 if (endOfName == NULL) { 77 LOGE("End of executable name not found."); 78 return 0; 79 } 80 char* state = endOfName + 2; 81 if ((state - buffer) + 1 > size) { 82 LOGE("Unexpected EOF while trying to read stat file."); 83 return 0; 84 } 85 thread->stateOffset = state - buffer; 86 return *state; 87 } 88 89 /* 90 * Looks for the state char at the last place we found it. Read from the 91 * beginning if necessary. 92 */ 93 static char readStateRelatively(SystemThread* thread) { 94 char buffer[3]; 95 // Position file offset at end of executable name. 96 int result = lseek(thread->statFile, thread->stateOffset - 2, SEEK_SET); 97 if (result < 0) { 98 LOGE("lseek() error."); 99 return 0; 100 } 101 int size = read(thread->statFile, buffer, sizeof(buffer)); 102 if (size < (int) sizeof(buffer)) { 103 LOGE("Unexpected EOF while trying to read stat file."); 104 return 0; 105 } 106 if (buffer[0] != ')') { 107 // The executable name must have changed. 108 result = lseek(thread->statFile, 0, SEEK_SET); 109 if (result < 0) { 110 LOGE("lseek() error."); 111 return 0; 112 } 113 return readStateFromBeginning(thread); 114 } 115 return buffer[2]; 116 } 117 118 ThreadStatus dvmGetSystemThreadStatus(Thread* thread) { 119 ThreadStatus status = thread->status; 120 if (status != THREAD_NATIVE) { 121 // Return cached status so we don't accidentally return THREAD_NATIVE. 122 return status; 123 } 124 125 if (thread->systemThread == NULL) { 126 thread->systemThread = (SystemThread*) malloc(sizeof(SystemThread)); 127 if (thread->systemThread == NULL) { 128 LOGE("Couldn't allocate a SystemThread."); 129 return THREAD_NATIVE; 130 } 131 thread->systemThread->statFile = -1; 132 } 133 134 SystemThread* systemThread = thread->systemThread; 135 if (systemThread->statFile == -2) { 136 // We tried and failed to open the file earlier. Return current status. 137 return thread->status; 138 } 139 140 // Note: see "man proc" for the format of stat. 141 // The format is "PID (EXECUTABLE NAME) STATE_CHAR ...". 142 // Example: "15 (/foo/bar) R ..." 143 char state; 144 if (systemThread->statFile == -1) { 145 // We haven't tried to open the file yet. Do so. 146 char fileName[256]; 147 sprintf(fileName, "/proc/self/task/%d/stat", thread->systemTid); 148 systemThread->statFile = open(fileName, O_RDONLY); 149 if (systemThread->statFile == -1) { 150 LOGE("Error opening %s: %s", fileName, strerror(errno)); 151 systemThread->statFile = -2; 152 return thread->status; 153 } 154 state = readStateFromBeginning(systemThread); 155 } else { 156 state = readStateRelatively(systemThread); 157 } 158 159 if (state == 0) { 160 close(systemThread->statFile); 161 systemThread->statFile = -2; 162 return thread->status; 163 } 164 ThreadStatus nativeStatus = stateToStatus(state); 165 166 // The thread status could have changed from NATIVE. 167 status = thread->status; 168 return status == THREAD_NATIVE ? nativeStatus : status; 169 } 170