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
dvmDetachSystemThread(Thread * thread)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. */
stateToStatus(char state)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. */
readStateFromBeginning(SystemThread * thread)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 */
readStateRelatively(SystemThread * thread)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
dvmGetSystemThreadStatus(Thread * thread)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