/*
* 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;
}