| // Copyright 2019 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| /** | |
| * @fileoverview Utilities for manipulating and introspecting into tasks. | |
| */ | |
| fidl.loadLibrary('fuchsia.kernel'); | |
| /** | |
| * Walks the tree of tasks (excluding threads) from a given zx.Job. | |
| * | |
| * @param {zx.Job} job The job from which to walk the tree. | |
| * @param {callback} preCallback Before iterating over the children of a zx.Task, is passed that | |
| * zx.Task | |
| * @param {callback} postCallback After iterating over the children of a zx.Task, is passed that | |
| * zx.Task | |
| */ | |
| function walkJobTree(job, preCallback, postCallback) { | |
| preCallback(job); | |
| let childrenKoids = job.getChildrenInfo(); | |
| for (let i = 0; i < childrenKoids.length; i++) { | |
| let childHandle = job.getChild(childrenKoids[i], zx.ZX_RIGHT_SAME_RIGHTS); | |
| walkJobTree(new zx.Job(childHandle), preCallback, postCallback); | |
| } | |
| let processesKoids = job.getProcessesInfo(); | |
| for (let i = 0; i < processesKoids.length; i++) { | |
| let processHandle = job.getChild(processesKoids[i], zx.ZX_RIGHT_SAME_RIGHTS); | |
| let process = new zx.Process(processHandle); | |
| preCallback(process); | |
| postCallback(process); | |
| } | |
| postCallback(job); | |
| } | |
| /** | |
| * Represents metadata about a given task. | |
| */ | |
| class TaskInfo { | |
| constructor(task) { | |
| this.info = task.getBasicInfo(); | |
| this.name = task.getProperty(zx.ZX_PROP_NAME); | |
| } | |
| } | |
| /** | |
| * Pretty prints a process tree to stdout. | |
| * | |
| * @param {Number} level How many spaces to print at the beginning of the first task. | |
| * @param {TaskInfo} rootObject Info about the root-level task | |
| * @param {Map} map Map from TaskInfo -> TaskInfo[] children. | |
| */ | |
| function printProcessMap(level, rootObject, map) { | |
| let s = ''; | |
| for (let j = 0; j < level; j++) { | |
| s += ' '; | |
| } | |
| s += rootObject.info.koid + ' ' + rootObject.name + '\n'; | |
| const children = map.get(rootObject); | |
| if (children != undefined) { | |
| for (let i = 0; i < children.length; i++) { | |
| s += printProcessMap(level + 1, children[i], map); | |
| } | |
| } | |
| return s; | |
| } | |
| function psJob(rootJob) { | |
| // A map from TaskInfo to a list of child TaskInfo objects. | |
| let jobMap = new Map(); | |
| // A stack of objects, maintained as we walk the job tree. | |
| let currObject = []; | |
| // The root object. An array so we can modify it in the callback. | |
| let rootObject = []; | |
| walkJobTree( | |
| rootJob, | |
| (object) => { | |
| let taskInfo = new TaskInfo(object); | |
| if (currObject.length == 0) { | |
| rootObject.push(taskInfo); | |
| } else { | |
| let val = jobMap.get(currObject[currObject.length - 1]); | |
| val.push(taskInfo); | |
| jobMap.set(currObject[currObject.length - 1], val); | |
| } | |
| currObject.push(taskInfo); | |
| jobMap.set(currObject[currObject.length - 1], []); | |
| }, | |
| (object) => { | |
| currObject.pop(); | |
| object.close(); | |
| }); | |
| jobMap.toString = () => { | |
| return printProcessMap(0, rootObject[0], jobMap); | |
| }; | |
| jobMap.root = rootObject[0]; | |
| return jobMap; | |
| } | |
| /** | |
| * Returns Map from TaskInfo to TaskInfo[], where the key is a given task, | |
| * and the value is its children. The root task is in the map with value root. | |
| */ | |
| async function ps() { | |
| let rootJobResult = await svc.fuchsia_kernel_RootJob.Get(); | |
| return psJob(rootJobResult['job']); | |
| } | |
| /** | |
| * Throws an error if no task had the given koid | |
| * @param {Number} taskToKillId the task koid | |
| */ | |
| async function kill(taskToKillId) { | |
| let rootJobResult = await svc.fuchsia_kernel_RootJob.Get(); | |
| // handle to the task to kill. An array so we can modify it in the callback. | |
| let killedTask = false; | |
| walkJobTree(rootJobResult['job'], (o) => {}, (task) => { | |
| if (task.getBasicInfo().koid == taskToKillId) { | |
| task.kill(); | |
| killedTask = true; | |
| } | |
| }); | |
| if (!killedTask) { | |
| throw 'No task with given id'; | |
| } | |
| } | |
| /** | |
| * Throws an error if no task matched the given name or regex | |
| * @param {String} taskName task name, will be interpreted as a regex if "r" is in the options | |
| * @param {String} options defaults to empty string, can be 'r' to interpret task name as regex | |
| */ | |
| async function killall(taskName, options = '') { | |
| let rootJobResult = await svc.fuchsia_kernel_RootJob.Get(); | |
| // handle to the task to kill. An array so we can modify it in the callback. | |
| let killedTask = false; | |
| let matchAsRegex = options.includes('r'); | |
| walkJobTree(rootJobResult['job'], (o) => {}, (task) => { | |
| if (matchAsRegex && task.getProperty(zx.ZX_PROP_NAME).match(taskName)) { | |
| task.kill(); | |
| killedTask = true; | |
| } else if (!matchAsRegex && task.getProperty(zx.ZX_PROP_NAME) == taskName) { | |
| task.kill(); | |
| killedTask = true; | |
| } | |
| }); | |
| if (!killedTask) { | |
| throw ('No task with given id'); | |
| } | |
| } | |
| export {ps, psJob, kill, killall}; |