/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "SystemThread" /* * System thread support. */ #include "Dalvik.h" #include "native/SystemThread.h" #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> struct SystemThread { /* * /proc/PID/task/TID/stat. -1 if not opened yet. -2 indicates an error * occurred while opening the file. */ int statFile; /* Offset of state char in stat file, last we checked. */ int stateOffset; }; void dvmDetachSystemThread(Thread* thread) { if (thread->systemThread != NULL) { if (thread->systemThread->statFile > -1) { close(thread->systemThread->statFile); } free(thread->systemThread); thread->systemThread = NULL; } } /* Converts a Linux thread state to a ThreadStatus. */ static ThreadStatus stateToStatus(char state) { switch (state) { case 'R': return THREAD_RUNNING; // running case 'S': return THREAD_WAIT; // sleeping in interruptible wait case 'D': return THREAD_WAIT; // uninterruptible disk sleep case 'Z': return THREAD_ZOMBIE; // zombie case 'T': return THREAD_WAIT; // traced or stopped on a signal case 'W': return THREAD_WAIT; // paging memory default: LOGE("Unexpected state: %c", state); return THREAD_NATIVE; } } /* Reads the state char starting from the beginning of the file. */ static char readStateFromBeginning(SystemThread* thread) { char buffer[256]; int size = read(thread->statFile, buffer, sizeof(buffer) - 1); if (size <= 0) { LOGE("read() returned %d: %s", size, strerror(errno)); return 0; } char* endOfName = (char*) memchr(buffer, ')', size); if (endOfName == NULL) { LOGE("End of executable name not found."); return 0; } char* state = endOfName + 2; if ((state - buffer) + 1 > size) { LOGE("Unexpected EOF while trying to read stat file."); return 0; } thread->stateOffset = state - buffer; return *state; } /* * Looks for the state char at the last place we found it. Read from the * beginning if necessary. */ static char readStateRelatively(SystemThread* thread) { char buffer[3]; // Position file offset at end of executable name. int result = lseek(thread->statFile, thread->stateOffset - 2, SEEK_SET); if (result < 0) { LOGE("lseek() error."); return 0; } int size = read(thread->statFile, buffer, sizeof(buffer)); if (size < (int) sizeof(buffer)) { LOGE("Unexpected EOF while trying to read stat file."); return 0; } if (buffer[0] != ')') { // The executable name must have changed. result = lseek(thread->statFile, 0, SEEK_SET); if (result < 0) { LOGE("lseek() error."); return 0; } return readStateFromBeginning(thread); } return buffer[2]; } ThreadStatus dvmGetSystemThreadStatus(Thread* thread) { ThreadStatus status = thread->status; if (status != THREAD_NATIVE) { // Return cached status so we don't accidentally return THREAD_NATIVE. return status; } if (thread->systemThread == NULL) { thread->systemThread = (SystemThread*) malloc(sizeof(SystemThread)); if (thread->systemThread == NULL) { LOGE("Couldn't allocate a SystemThread."); return THREAD_NATIVE; } thread->systemThread->statFile = -1; } SystemThread* systemThread = thread->systemThread; if (systemThread->statFile == -2) { // We tried and failed to open the file earlier. Return current status. return thread->status; } // Note: see "man proc" for the format of stat. // The format is "PID (EXECUTABLE NAME) STATE_CHAR ...". // Example: "15 (/foo/bar) R ..." char state; if (systemThread->statFile == -1) { // We haven't tried to open the file yet. Do so. char fileName[256]; sprintf(fileName, "/proc/self/task/%d/stat", thread->systemTid); systemThread->statFile = open(fileName, O_RDONLY); if (systemThread->statFile == -1) { LOGE("Error opening %s: %s", fileName, strerror(errno)); systemThread->statFile = -2; return thread->status; } state = readStateFromBeginning(systemThread); } else { state = readStateRelatively(systemThread); } if (state == 0) { close(systemThread->statFile); systemThread->statFile = -2; return thread->status; } ThreadStatus nativeStatus = stateToStatus(state); // The thread status could have changed from NATIVE. status = thread->status; return status == THREAD_NATIVE ? nativeStatus : status; }