android_util_Process.cpp revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
1/* //device/libs/android_runtime/android_util_Process.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#define LOG_TAG "Process"
19
20#include <utils/Log.h>
21#include <utils/IPCThreadState.h>
22#include <utils/ProcessState.h>
23#include <utils/IServiceManager.h>
24#include <utils/String8.h>
25#include <utils/Vector.h>
26
27#include <android_runtime/AndroidRuntime.h>
28
29#include "android_util_Binder.h"
30#include "JNIHelp.h"
31
32#include <sys/errno.h>
33#include <sys/resource.h>
34#include <sys/types.h>
35#include <dirent.h>
36#include <fcntl.h>
37#include <grp.h>
38#include <pwd.h>
39#include <signal.h>
40
41/* desktop Linux needs a little help with gettid() */
42#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
43#define __KERNEL__
44# include <linux/unistd.h>
45#ifdef _syscall0
46_syscall0(pid_t,gettid)
47#else
48pid_t gettid() { return syscall(__NR_gettid);}
49#endif
50#undef __KERNEL__
51#endif
52
53using namespace android;
54
55static void signalExceptionForPriorityError(JNIEnv* env, jobject obj, int err)
56{
57    switch (err) {
58        case EINVAL:
59            jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
60            break;
61        case ESRCH:
62            jniThrowException(env, "java/lang/IllegalArgumentException", "Given thread does not exist");
63            break;
64        case EPERM:
65            jniThrowException(env, "java/lang/SecurityException", "No permission to modify given thread");
66            break;
67        case EACCES:
68            jniThrowException(env, "java/lang/SecurityException", "No permission to set to given priority");
69            break;
70        default:
71            jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
72            break;
73    }
74}
75
76static void fakeProcessEntry(void* arg)
77{
78    String8* cls = (String8*)arg;
79
80    AndroidRuntime* jr = AndroidRuntime::getRuntime();
81    jr->callMain(cls->string(), 0, NULL);
82
83    delete cls;
84}
85
86jint android_os_Process_myPid(JNIEnv* env, jobject clazz)
87{
88    return getpid();
89}
90
91jint android_os_Process_myUid(JNIEnv* env, jobject clazz)
92{
93    return getuid();
94}
95
96jint android_os_Process_myTid(JNIEnv* env, jobject clazz)
97{
98#ifdef HAVE_GETTID
99    return gettid();
100#else
101    return getpid();
102#endif
103}
104
105jint android_os_Process_getUidForName(JNIEnv* env, jobject clazz, jstring name)
106{
107    if (name == NULL) {
108        jniThrowException(env, "java/lang/NullPointerException", NULL);
109        return -1;
110    }
111
112    const jchar* str16 = env->GetStringCritical(name, 0);
113    String8 name8;
114    if (str16) {
115        name8 = String8(str16, env->GetStringLength(name));
116        env->ReleaseStringCritical(name, str16);
117    }
118
119    const size_t N = name8.size();
120    if (N > 0) {
121        const char* str = name8.string();
122        for (size_t i=0; i<N; i++) {
123            if (str[i] < '0' || str[i] > '9') {
124                struct passwd* pwd = getpwnam(str);
125                if (pwd == NULL) {
126                    return -1;
127                }
128                return pwd->pw_uid;
129            }
130        }
131        return atoi(str);
132    }
133    return -1;
134}
135
136jint android_os_Process_getGidForName(JNIEnv* env, jobject clazz, jstring name)
137{
138    if (name == NULL) {
139        jniThrowException(env, "java/lang/NullPointerException", NULL);
140        return -1;
141    }
142
143    const jchar* str16 = env->GetStringCritical(name, 0);
144    String8 name8;
145    if (str16) {
146        name8 = String8(str16, env->GetStringLength(name));
147        env->ReleaseStringCritical(name, str16);
148    }
149
150    const size_t N = name8.size();
151    if (N > 0) {
152        const char* str = name8.string();
153        for (size_t i=0; i<N; i++) {
154            if (str[i] < '0' || str[i] > '9') {
155                struct group* grp = getgrnam(str);
156                if (grp == NULL) {
157                    return -1;
158                }
159                return grp->gr_gid;
160            }
161        }
162        return atoi(str);
163    }
164    return -1;
165}
166
167void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
168                                              jint pid, jint pri)
169{
170    if (setpriority(PRIO_PROCESS, pid, pri) < 0) {
171        signalExceptionForPriorityError(env, clazz, errno);
172    }
173    //LOGI("Setting priority of %d: %d, getpriority returns %d\n",
174    //     pid, pri, getpriority(PRIO_PROCESS, pid));
175}
176
177void android_os_Process_setCallingThreadPriority(JNIEnv* env, jobject clazz,
178                                                        jint pri)
179{
180    jint tid = android_os_Process_myTid(env, clazz);
181    android_os_Process_setThreadPriority(env, clazz, tid, pri);
182}
183
184jint android_os_Process_getThreadPriority(JNIEnv* env, jobject clazz,
185                                              jint pid)
186{
187    errno = 0;
188    jint pri = getpriority(PRIO_PROCESS, pid);
189    if (errno != 0) {
190        signalExceptionForPriorityError(env, clazz, errno);
191    }
192    //LOGI("Returning priority of %d: %d\n", pid, pri);
193    return pri;
194}
195
196jboolean android_os_Process_setOomAdj(JNIEnv* env, jobject clazz,
197                                      jint pid, jint adj)
198{
199#ifdef HAVE_OOM_ADJ
200    if (ProcessState::self()->supportsProcesses()) {
201        char text[64];
202        sprintf(text, "/proc/%d/oom_adj", pid);
203        int fd = open(text, O_WRONLY);
204        if (fd >= 0) {
205            sprintf(text, "%d", adj);
206            write(fd, text, strlen(text));
207            close(fd);
208            return true;
209        }
210    }
211#endif
212    return false;
213}
214
215void android_os_Process_setArgV0(JNIEnv* env, jobject clazz, jstring name)
216{
217    if (name == NULL) {
218        jniThrowException(env, "java/lang/NullPointerException", NULL);
219        return;
220    }
221
222    const jchar* str = env->GetStringCritical(name, 0);
223    String8 name8;
224    if (str) {
225        name8 = String8(str, env->GetStringLength(name));
226        env->ReleaseStringCritical(name, str);
227    }
228
229    if (name8.size() > 0) {
230        ProcessState::self()->setArgV0(name8.string());
231    }
232}
233
234jint android_os_Process_setUid(JNIEnv* env, jobject clazz, jint uid)
235{
236    #if HAVE_ANDROID_OS
237    return setuid(uid) == 0 ? 0 : errno;
238    #else
239    return ENOSYS;
240    #endif
241}
242
243jint android_os_Process_setGid(JNIEnv* env, jobject clazz, jint uid)
244{
245    #if HAVE_ANDROID_OS
246    return setgid(uid) == 0 ? 0 : errno;
247    #else
248    return ENOSYS;
249    #endif
250}
251
252jboolean android_os_Process_supportsProcesses(JNIEnv* env, jobject clazz)
253{
254    return ProcessState::self()->supportsProcesses();
255}
256
257static int pid_compare(const void* v1, const void* v2)
258{
259    //LOGI("Compare %d vs %d\n", *((const jint*)v1), *((const jint*)v2));
260    return *((const jint*)v1) - *((const jint*)v2);
261}
262
263jint android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
264{
265    int fd = open("/proc/meminfo", O_RDONLY);
266
267    if (fd < 0) {
268        LOGW("Unable to open /proc/meminfo");
269        return -1;
270    }
271
272    char buffer[256];
273    const int len = read(fd, buffer, sizeof(buffer)-1);
274    close(fd);
275
276    if (len < 0) {
277        LOGW("Unable to read /proc/meminfo");
278        return -1;
279    }
280    buffer[len] = 0;
281
282    int numFound = 0;
283    int mem = 0;
284
285    static const char* const sums[] = { "MemFree:", "Cached:", NULL };
286    static const int sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), NULL };
287
288    char* p = buffer;
289    while (*p && numFound < 2) {
290        int i = 0;
291        while (sums[i]) {
292            if (strncmp(p, sums[i], sumsLen[i]) == 0) {
293                p += sumsLen[i];
294                while (*p == ' ') p++;
295                char* num = p;
296                while (*p >= '0' && *p <= '9') p++;
297                if (*p != 0) {
298                    *p = 0;
299                    p++;
300                    if (*p == 0) p--;
301                }
302                mem += atoi(num) * 1024;
303                numFound++;
304                break;
305            }
306            i++;
307        }
308        p++;
309    }
310
311    return numFound > 0 ? mem : -1;
312}
313
314void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileStr,
315                                      jobjectArray reqFields, jlongArray outFields)
316{
317    //LOGI("getMemInfo: %p %p", reqFields, outFields);
318
319    if (fileStr == NULL || reqFields == NULL || outFields == NULL) {
320        jniThrowException(env, "java/lang/NullPointerException", NULL);
321        return;
322    }
323
324    const char* file8 = env->GetStringUTFChars(fileStr, NULL);
325    if (file8 == NULL) {
326        return;
327    }
328    String8 file(file8);
329    env->ReleaseStringUTFChars(fileStr, file8);
330
331    jsize count = env->GetArrayLength(reqFields);
332    if (count > env->GetArrayLength(outFields)) {
333        jniThrowException(env, "java/lang/IllegalArgumentException", "Array lengths differ");
334        return;
335    }
336
337    Vector<String8> fields;
338    int i;
339
340    for (i=0; i<count; i++) {
341        jobject obj = env->GetObjectArrayElement(reqFields, i);
342        if (obj != NULL) {
343            const char* str8 = env->GetStringUTFChars((jstring)obj, NULL);
344            //LOGI("String at %d: %p = %s", i, obj, str8);
345            if (str8 == NULL) {
346                jniThrowException(env, "java/lang/NullPointerException", "Element in reqFields");
347                return;
348            }
349            fields.add(String8(str8));
350            env->ReleaseStringUTFChars((jstring)obj, str8);
351        } else {
352            jniThrowException(env, "java/lang/NullPointerException", "Element in reqFields");
353            return;
354        }
355    }
356
357    jlong* sizesArray = env->GetLongArrayElements(outFields, 0);
358    if (sizesArray == NULL) {
359        return;
360    }
361
362    //LOGI("Clearing %d sizes", count);
363    for (i=0; i<count; i++) {
364        sizesArray[i] = 0;
365    }
366
367    int fd = open(file.string(), O_RDONLY);
368
369    if (fd >= 0) {
370        const size_t BUFFER_SIZE = 2048;
371        char* buffer = (char*)malloc(BUFFER_SIZE);
372        int len = read(fd, buffer, BUFFER_SIZE-1);
373        close(fd);
374
375        if (len < 0) {
376            LOGW("Unable to read %s", file.string());
377            len = 0;
378        }
379        buffer[len] = 0;
380
381        int foundCount = 0;
382
383        char* p = buffer;
384        while (*p && foundCount < count) {
385            bool skipToEol = true;
386            //LOGI("Parsing at: %s", p);
387            for (i=0; i<count; i++) {
388                const String8& field = fields[i];
389                if (strncmp(p, field.string(), field.length()) == 0) {
390                    p += field.length();
391                    while (*p == ' ') p++;
392                    char* num = p;
393                    while (*p >= '0' && *p <= '9') p++;
394                    skipToEol = *p != '\n';
395                    if (*p != 0) {
396                        *p = 0;
397                        p++;
398                    }
399                    char* end;
400                    sizesArray[i] = strtoll(num, &end, 10);
401                    //LOGI("Field %s = %d", field.string(), sizesArray[i]);
402                    foundCount++;
403                    break;
404                }
405            }
406            if (skipToEol) {
407                while (*p && *p != '\n') {
408                    p++;
409                }
410                if (*p == '\n') {
411                    p++;
412                }
413            }
414        }
415
416        free(buffer);
417    } else {
418        LOGW("Unable to open %s", file.string());
419    }
420
421    //LOGI("Done!");
422    env->ReleaseLongArrayElements(outFields, sizesArray, 0);
423}
424
425jintArray android_os_Process_getPids(JNIEnv* env, jobject clazz,
426                                     jstring file, jintArray lastArray)
427{
428    if (file == NULL) {
429        jniThrowException(env, "java/lang/NullPointerException", NULL);
430        return NULL;
431    }
432
433    const char* file8 = env->GetStringUTFChars(file, NULL);
434    if (file8 == NULL) {
435        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
436        return NULL;
437    }
438
439    DIR* dirp = opendir(file8);
440
441    env->ReleaseStringUTFChars(file, file8);
442
443    if(dirp == NULL) {
444        return NULL;
445    }
446
447    jsize curCount = 0;
448    jint* curData = NULL;
449    if (lastArray != NULL) {
450        curCount = env->GetArrayLength(lastArray);
451        curData = env->GetIntArrayElements(lastArray, 0);
452    }
453
454    jint curPos = 0;
455
456    struct dirent* entry;
457    while ((entry=readdir(dirp)) != NULL) {
458        const char* p = entry->d_name;
459        while (*p) {
460            if (*p < '0' || *p > '9') break;
461            p++;
462        }
463        if (*p != 0) continue;
464
465        char* end;
466        int pid = strtol(entry->d_name, &end, 10);
467        //LOGI("File %s pid=%d\n", entry->d_name, pid);
468        if (curPos >= curCount) {
469            jsize newCount = (curCount == 0) ? 10 : (curCount*2);
470            jintArray newArray = env->NewIntArray(newCount);
471            if (newArray == NULL) {
472                closedir(dirp);
473                jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
474                return NULL;
475            }
476            jint* newData = env->GetIntArrayElements(newArray, 0);
477            if (curData != NULL) {
478                memcpy(newData, curData, sizeof(jint)*curCount);
479                env->ReleaseIntArrayElements(lastArray, curData, 0);
480            }
481            lastArray = newArray;
482            curCount = newCount;
483            curData = newData;
484        }
485
486        curData[curPos] = pid;
487        curPos++;
488    }
489
490    closedir(dirp);
491
492    if (curData != NULL && curPos > 0) {
493        qsort(curData, curPos, sizeof(jint), pid_compare);
494    }
495
496    while (curPos < curCount) {
497        curData[curPos] = -1;
498        curPos++;
499    }
500
501    if (curData != NULL) {
502        env->ReleaseIntArrayElements(lastArray, curData, 0);
503    }
504
505    return lastArray;
506}
507
508enum {
509    PROC_TERM_MASK = 0xff,
510    PROC_ZERO_TERM = 0,
511    PROC_SPACE_TERM = ' ',
512    PROC_COMBINE = 0x100,
513    PROC_PARENS = 0x200,
514    PROC_OUT_STRING = 0x1000,
515    PROC_OUT_LONG = 0x2000,
516    PROC_OUT_FLOAT = 0x4000,
517};
518
519jboolean android_os_Process_readProcFile(JNIEnv* env, jobject clazz,
520        jstring file, jintArray format, jobjectArray outStrings,
521        jlongArray outLongs, jfloatArray outFloats)
522{
523    if (file == NULL || format == NULL) {
524        jniThrowException(env, "java/lang/NullPointerException", NULL);
525        return JNI_FALSE;
526    }
527
528    const char* file8 = env->GetStringUTFChars(file, NULL);
529    if (file8 == NULL) {
530        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
531        return JNI_FALSE;
532    }
533    int fd = open(file8, O_RDONLY);
534    env->ReleaseStringUTFChars(file, file8);
535
536    if (fd < 0) {
537        //LOGW("Unable to open process file: %s\n", file8);
538        return JNI_FALSE;
539    }
540
541    char buffer[256];
542    const int len = read(fd, buffer, sizeof(buffer)-1);
543    close(fd);
544
545    if (len < 0) {
546        //LOGW("Unable to open process file: %s fd=%d\n", file8, fd);
547        return JNI_FALSE;
548    }
549    buffer[len] = 0;
550
551    //LOGI("Process file %s: %s\n", file8, buffer);
552
553    const jsize NF = env->GetArrayLength(format);
554    const jsize NS = outStrings ? env->GetArrayLength(outStrings) : 0;
555    const jsize NL = outLongs ? env->GetArrayLength(outLongs) : 0;
556    const jsize NR = outFloats ? env->GetArrayLength(outFloats) : 0;
557
558    jint* formatData = env->GetIntArrayElements(format, 0);
559    jlong* longsData = outLongs ?
560        env->GetLongArrayElements(outLongs, 0) : NULL;
561    jfloat* floatsData = outFloats ?
562        env->GetFloatArrayElements(outFloats, 0) : NULL;
563    if (formatData == NULL || (NL > 0 && longsData == NULL)
564            || (NR > 0 && floatsData == NULL)) {
565        if (formatData != NULL) {
566            env->ReleaseIntArrayElements(format, formatData, 0);
567        }
568        if (longsData != NULL) {
569            env->ReleaseLongArrayElements(outLongs, longsData, 0);
570        }
571        if (floatsData != NULL) {
572            env->ReleaseFloatArrayElements(outFloats, floatsData, 0);
573        }
574        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
575        return JNI_FALSE;
576    }
577
578    jsize i = 0;
579    jsize di = 0;
580
581    jboolean res = JNI_TRUE;
582
583    for (jsize fi=0; fi<NF; fi++) {
584        const jint mode = formatData[fi];
585        if ((mode&PROC_PARENS) != 0) {
586            i++;
587        }
588        const char term = (char)(mode&PROC_TERM_MASK);
589        const jsize start = i;
590        if (i >= len) {
591            res = JNI_FALSE;
592            break;
593        }
594
595        jsize end = -1;
596        if ((mode&PROC_PARENS) != 0) {
597            while (buffer[i] != ')' && i < len) {
598                i++;
599            }
600            end = i;
601            i++;
602        }
603        while (buffer[i] != term && i < len) {
604            i++;
605        }
606        if (end < 0) {
607            end = i;
608        }
609
610        if (i < len) {
611            i++;
612            if ((mode&PROC_COMBINE) != 0) {
613                while (buffer[i] == term && i < len) {
614                    i++;
615                }
616            }
617        }
618
619        //LOGI("Field %d: %d-%d dest=%d mode=0x%x\n", i, start, end, di, mode);
620
621        if ((mode&(PROC_OUT_FLOAT|PROC_OUT_LONG|PROC_OUT_STRING)) != 0) {
622            char c = buffer[end];
623            buffer[end] = 0;
624            if ((mode&PROC_OUT_FLOAT) != 0 && di < NR) {
625                char* end;
626                floatsData[di] = strtof(buffer+start, &end);
627            }
628            if ((mode&PROC_OUT_LONG) != 0 && di < NL) {
629                char* end;
630                longsData[di] = strtoll(buffer+start, &end, 10);
631            }
632            if ((mode&PROC_OUT_STRING) != 0 && di < NS) {
633                jstring str = env->NewStringUTF(buffer+start);
634                env->SetObjectArrayElement(outStrings, di, str);
635            }
636            buffer[end] = c;
637            di++;
638        }
639    }
640
641    env->ReleaseIntArrayElements(format, formatData, 0);
642    if (longsData != NULL) {
643        env->ReleaseLongArrayElements(outLongs, longsData, 0);
644    }
645    if (floatsData != NULL) {
646        env->ReleaseFloatArrayElements(outFloats, floatsData, 0);
647    }
648
649    return res;
650}
651
652void android_os_Process_setApplicationObject(JNIEnv* env, jobject clazz,
653                                             jobject binderObject)
654{
655    if (binderObject == NULL) {
656        jniThrowException(env, "java/lang/NullPointerException", NULL);
657        return;
658    }
659
660    sp<IBinder> binder = ibinderForJavaObject(env, binderObject);
661}
662
663void android_os_Process_sendSignal(JNIEnv* env, jobject clazz, jint pid, jint sig)
664{
665    if (pid > 0) {
666        LOGI("Sending signal. PID: %d SIG: %d", pid, sig);
667        kill(pid, sig);
668    }
669}
670
671static jlong android_os_Process_getElapsedCpuTime(JNIEnv* env, jobject clazz)
672{
673    struct timespec ts;
674
675    int res = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
676
677    if (res != 0) {
678        return (jlong) 0;
679    }
680
681    nsecs_t when = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec;
682    return (jlong) nanoseconds_to_milliseconds(when);
683}
684
685static jlong android_os_Process_getPss(JNIEnv* env, jobject clazz, jint pid)
686{
687    char filename[64];
688
689    snprintf(filename, sizeof(filename), "/proc/%d/smaps", pid);
690
691    FILE * file = fopen(filename, "r");
692    if (!file) {
693        return (jlong) -1;
694    }
695
696    // Tally up all of the Pss from the various maps
697    char line[256];
698    jlong pss = 0;
699    while (fgets(line, sizeof(line), file)) {
700        jlong v;
701        if (sscanf(line, "Pss: %lld kB", &v) == 1) {
702            pss += v;
703        }
704    }
705
706    fclose(file);
707
708    // Return the Pss value in bytes, not kilobytes
709    return pss * 1024;
710}
711
712static const JNINativeMethod methods[] = {
713    {"myPid",       "()I", (void*)android_os_Process_myPid},
714    {"myTid",       "()I", (void*)android_os_Process_myTid},
715    {"myUid",       "()I", (void*)android_os_Process_myUid},
716    {"getUidForName",       "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName},
717    {"getGidForName",       "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName},
718    {"setThreadPriority",   "(II)V", (void*)android_os_Process_setThreadPriority},
719    {"setThreadPriority",   "(I)V", (void*)android_os_Process_setCallingThreadPriority},
720    {"getThreadPriority",   "(I)I", (void*)android_os_Process_getThreadPriority},
721    {"setOomAdj",   "(II)Z", (void*)android_os_Process_setOomAdj},
722    {"setArgV0",    "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0},
723    {"setUid", "(I)I", (void*)android_os_Process_setUid},
724    {"setGid", "(I)I", (void*)android_os_Process_setGid},
725    {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal},
726    {"supportsProcesses", "()Z", (void*)android_os_Process_supportsProcesses},
727    {"getFreeMemory", "()I", (void*)android_os_Process_getFreeMemory},
728    {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", (void*)android_os_Process_readProcLines},
729    {"getPids", "(Ljava/lang/String;[I)[I", (void*)android_os_Process_getPids},
730    {"readProcFile", "(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_readProcFile},
731    {"getElapsedCpuTime", "()J", (void*)android_os_Process_getElapsedCpuTime},
732    {"getPss", "(I)J", (void*)android_os_Process_getPss},
733    //{"setApplicationObject", "(Landroid/os/IBinder;)V", (void*)android_os_Process_setApplicationObject},
734};
735
736const char* const kProcessPathName = "android/os/Process";
737
738int register_android_os_Process(JNIEnv* env)
739{
740    jclass clazz;
741
742    clazz = env->FindClass(kProcessPathName);
743    LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Process");
744
745    return AndroidRuntime::registerNativeMethods(
746        env, kProcessPathName,
747        methods, NELEM(methods));
748}
749
750