Backed out 2 changesets (bug 1652000) for causing Bug 1653749.

Backed out changeset 69dbf38f4071 (bug 1652000)
Backed out changeset 0a6cb920a247 (bug 1652000)
This commit is contained in:
Cosmin Sabou 2020-07-19 05:54:07 +03:00
Родитель 78c662789a
Коммит c562fee6cd
8 изменённых файлов: 438 добавлений и 598 удалений

Просмотреть файл

@ -781,7 +781,7 @@ already_AddRefed<Promise> ChromeUtils::RequestProcInfo(GlobalObject& aGlobal,
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
// Prepare the JS promise that will hold our response.
// Creating a JS promise
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
MOZ_ASSERT(global);
RefPtr<Promise> domPromise = Promise::Create(global, aRv);
@ -790,204 +790,213 @@ already_AddRefed<Promise> ChromeUtils::RequestProcInfo(GlobalObject& aGlobal,
}
MOZ_ASSERT(domPromise);
// Get a list of processes to examine and pre-fill them with available info.
// Note that this is subject to race conditions: just because we have a
// process in the list doesn't mean that the process will still be alive when
// we attempt to get its information. Followup code MUST be able to fail
// gracefully on some processes and still return whichever information is
// available.
// Get all the content parents.
// Note that this array includes even the long dead content parents, so we
// might have some garbage, especially with Fission.
// SAFETY NOTE: `contentParents` is only valid if used synchronously.
// Anything else and you may end up dealing with dangling pointers.
nsTArray<ContentParent*> contentParents;
ContentParent::GetAll(contentParents);
// Prepare our background request.
// We reserve one more slot for the browser process itself.
nsTArray<ProcInfoRequest> requests(contentParents.Length() + 1);
// Requesting process info for the browser process itself.
requests.EmplaceBack(
/* aPid = */ base::GetCurrentProcId(),
/* aProcessType = */ ProcType::Browser,
/* aOrigin = */ ""_ns);
mozilla::ipc::GeckoChildProcessHost::GetAll(
[&requests,
&contentParents](mozilla::ipc::GeckoChildProcessHost* aGeckoProcess) {
auto handle = aGeckoProcess->GetChildProcessHandle();
if (!handle) {
// Something went wrong with this process, it may be dead already,
// fail gracefully.
return;
}
nsAutoCString origin;
base::ProcessId childPid = base::GetProcId(handle);
int32_t childId = 0;
mozilla::ProcType type = mozilla::ProcType::Unknown;
switch (aGeckoProcess->GetProcessType()) {
case GeckoProcessType::GeckoProcessType_Content: {
ContentParent* contentParent = nullptr;
// This loop can become slow as we get more processes in
// Fission, so might need some refactoring in the future.
for (ContentParent* parent : contentParents) {
// find the match
if (parent->Process() == aGeckoProcess) {
contentParent = parent;
break;
}
}
if (!contentParent) {
// FIXME: When can this happen?
return;
}
// Converting the remoteType into a ProcType.
// Ideally, the remoteType should be strongly typed
// upstream, this would make the conversion less brittle.
nsAutoCString remoteType(contentParent->GetRemoteType());
if (StringBeginsWith(remoteType, FISSION_WEB_REMOTE_TYPE)) {
// WARNING: Do not change the order, as
// `DEFAULT_REMOTE_TYPE` is a prefix of
// `FISSION_WEB_REMOTE_TYPE`.
type = mozilla::ProcType::WebIsolated;
} else if (StringBeginsWith(remoteType, DEFAULT_REMOTE_TYPE)) {
type = mozilla::ProcType::Web;
} else if (remoteType == FILE_REMOTE_TYPE) {
type = mozilla::ProcType::File;
} else if (remoteType == EXTENSION_REMOTE_TYPE) {
type = mozilla::ProcType::Extension;
} else if (remoteType == PRIVILEGEDABOUT_REMOTE_TYPE) {
type = mozilla::ProcType::PrivilegedAbout;
} else if (remoteType == PRIVILEGEDMOZILLA_REMOTE_TYPE) {
type = mozilla::ProcType::PrivilegedMozilla;
} else if (StringBeginsWith(remoteType,
WITH_COOP_COEP_REMOTE_TYPE_PREFIX)) {
type = mozilla::ProcType::WebCOOPCOEP;
} else if (remoteType == LARGE_ALLOCATION_REMOTE_TYPE) {
type = mozilla::ProcType::WebLargeAllocation;
} else if (remoteType == PREALLOC_REMOTE_TYPE) {
type = mozilla::ProcType::Preallocated;
} else {
MOZ_CRASH_UNSAFE_PRINTF("Unknown remoteType '%s'",
remoteType.get());
}
// By convention, everything after '=' is the origin.
nsACString::const_iterator cursor;
nsACString::const_iterator end;
remoteType.BeginReading(cursor);
remoteType.EndReading(end);
if (FindCharInReadable('=', cursor, end)) {
origin = Substring(++cursor, end);
}
childId = contentParent->ChildID();
break;
}
case GeckoProcessType::GeckoProcessType_Default:
type = mozilla::ProcType::Browser;
break;
case GeckoProcessType::GeckoProcessType_Plugin:
type = mozilla::ProcType::Plugin;
break;
case GeckoProcessType::GeckoProcessType_GMPlugin:
type = mozilla::ProcType::GMPlugin;
break;
case GeckoProcessType::GeckoProcessType_GPU:
type = mozilla::ProcType::GPU;
break;
case GeckoProcessType::GeckoProcessType_VR:
type = mozilla::ProcType::VR;
break;
case GeckoProcessType::GeckoProcessType_RDD:
type = mozilla::ProcType::RDD;
break;
case GeckoProcessType::GeckoProcessType_Socket:
type = mozilla::ProcType::Socket;
break;
case GeckoProcessType::GeckoProcessType_RemoteSandboxBroker:
type = mozilla::ProcType::RemoteSandboxBroker;
break;
#ifdef MOZ_ENABLE_FORKSERVER
case GeckoProcessType::GeckoProcessType_ForkServer:
type = mozilla::ProcType::ForkServer;
break;
#endif
default:
// Leave the default Unknown value in |type|.
break;
}
requests.EmplaceBack(
/* aPid = */ childPid,
/* aProcessType = */ type,
/* aOrigin = */ origin,
/* aChild = */ childId
#ifdef XP_MACOSX
,
/* aChildTask = */ aGeckoProcess->GetChildTask()
#endif // XP_MACOSX
);
});
// Now place background request.
base::ProcessId parentPid = base::GetCurrentProcId();
RefPtr<nsISerialEventTarget> target =
global->EventTargetFor(TaskCategory::Performance);
mozilla::GetProcInfo(std::move(requests))
// Getting the parent proc info
mozilla::GetProcInfo(parentPid, 0, mozilla::ProcType::Browser, ""_ns)
->Then(
target, __func__,
[target,
domPromise](const HashMap<base::ProcessId, ProcInfo>& aSysProcInfo) {
ParentProcInfoDictionary parentInfo;
if (aSysProcInfo.count() == 0) {
// For some reason, we couldn't get *any* info.
// Maybe a sandboxing issue?
domPromise->MaybeReject(NS_ERROR_UNEXPECTED);
return;
}
nsTArray<ChildProcInfoDictionary> childrenInfo(
aSysProcInfo.count() - 1);
for (auto iter = aSysProcInfo.iter(); !iter.done(); iter.next()) {
const auto& sysProcInfo = iter.get().value();
nsresult rv;
if (sysProcInfo.type == ProcType::Browser) {
rv = mozilla::CopySysProcInfoToDOM(sysProcInfo, &parentInfo);
if (NS_FAILED(rv)) {
// Failing to copy? That's probably not something from we can
// (or should) try to recover gracefully.
domPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
return;
}
MOZ_ASSERT(sysProcInfo.childId == 0);
MOZ_ASSERT(sysProcInfo.origin.IsEmpty());
} else {
mozilla::dom::ChildProcInfoDictionary* childInfo =
childrenInfo.AppendElement(fallible);
if (!childInfo) {
domPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
return;
}
rv = mozilla::CopySysProcInfoToDOM(sysProcInfo, childInfo);
if (NS_FAILED(rv)) {
domPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
return;
}
// Copy Firefox info.
childInfo->mChildID = sysProcInfo.childId;
childInfo->mOrigin = sysProcInfo.origin;
childInfo->mType = ProcTypeToWebIDL(sysProcInfo.type);
}
}
[target, domPromise, parentPid](ProcInfo aParentInfo) {
// Get a list of ContentParent
nsTArray<ContentParent*> contentParents;
ContentParent::GetAll(contentParents);
nsTArray<RefPtr<ProcInfoPromise>> promises;
mozilla::ipc::GeckoChildProcessHost::GetAll(
[&promises, &contentParents](
mozilla::ipc::GeckoChildProcessHost* aGeckoProcess) {
if (!aGeckoProcess->GetChildProcessHandle()) {
return;
}
nsAutoCString origin;
base::ProcessId childPid =
base::GetProcId(aGeckoProcess->GetChildProcessHandle());
int32_t childId = 0;
mozilla::ProcType type = mozilla::ProcType::Unknown;
switch (aGeckoProcess->GetProcessType()) {
case GeckoProcessType::GeckoProcessType_Content: {
ContentParent* contentParent = nullptr;
// This loop can become slow as we get more processes in
// Fission, so might need some refactoring in the future.
for (ContentParent* parent : contentParents) {
// find the match
if (parent->Process() == aGeckoProcess) {
contentParent = parent;
break;
}
}
if (!contentParent) {
return;
}
// Converting the remoteType into a ProcType.
// Ideally, the remoteType should be strongly typed
// upstream, this would make the conversion less brittle.
nsAutoCString remoteType(contentParent->GetRemoteType());
if (StringBeginsWith(remoteType,
FISSION_WEB_REMOTE_TYPE)) {
// WARNING: Do not change the order, as
// `DEFAULT_REMOTE_TYPE` is a prefix of
// `FISSION_WEB_REMOTE_TYPE`.
type = mozilla::ProcType::WebIsolated;
} else if (StringBeginsWith(remoteType,
DEFAULT_REMOTE_TYPE)) {
type = mozilla::ProcType::Web;
} else if (remoteType == FILE_REMOTE_TYPE) {
type = mozilla::ProcType::File;
} else if (remoteType == EXTENSION_REMOTE_TYPE) {
type = mozilla::ProcType::Extension;
} else if (remoteType == PRIVILEGEDABOUT_REMOTE_TYPE) {
type = mozilla::ProcType::PrivilegedAbout;
} else if (remoteType == PRIVILEGEDMOZILLA_REMOTE_TYPE) {
type = mozilla::ProcType::PrivilegedMozilla;
} else if (StringBeginsWith(
remoteType,
WITH_COOP_COEP_REMOTE_TYPE_PREFIX)) {
type = mozilla::ProcType::WebCOOPCOEP;
} else if (remoteType == LARGE_ALLOCATION_REMOTE_TYPE) {
type = mozilla::ProcType::WebLargeAllocation;
} else if (remoteType == PREALLOC_REMOTE_TYPE) {
type = mozilla::ProcType::Preallocated;
} else {
MOZ_CRASH("Unknown remoteType");
}
// Attach the children to the parent.
mozilla::dom::Sequence<mozilla::dom::ChildProcInfoDictionary>
children(std::move(childrenInfo));
parentInfo.mChildren = std::move(children);
domPromise->MaybeResolve(parentInfo);
// By convention, everything after '=' is the origin.
nsACString::const_iterator cursor;
nsACString::const_iterator end;
remoteType.BeginReading(cursor);
remoteType.EndReading(end);
if (FindCharInReadable('=', cursor, end)) {
origin = Substring(++cursor, end);
}
childId = contentParent->ChildID();
break;
}
case GeckoProcessType::GeckoProcessType_Default:
type = mozilla::ProcType::Browser;
break;
case GeckoProcessType::GeckoProcessType_Plugin:
type = mozilla::ProcType::Plugin;
break;
case GeckoProcessType::GeckoProcessType_GMPlugin:
type = mozilla::ProcType::GMPlugin;
break;
case GeckoProcessType::GeckoProcessType_GPU:
type = mozilla::ProcType::GPU;
break;
case GeckoProcessType::GeckoProcessType_VR:
type = mozilla::ProcType::VR;
break;
case GeckoProcessType::GeckoProcessType_RDD:
type = mozilla::ProcType::RDD;
break;
case GeckoProcessType::GeckoProcessType_Socket:
type = mozilla::ProcType::Socket;
break;
case GeckoProcessType::GeckoProcessType_RemoteSandboxBroker:
type = mozilla::ProcType::RemoteSandboxBroker;
break;
#ifdef MOZ_ENABLE_FORKSERVER
case GeckoProcessType::GeckoProcessType_ForkServer:
type = mozilla::ProcType::ForkServer;
break;
#endif
default:
// Leave the default Unknown value in |type|.
break;
}
promises.AppendElement(
#ifdef XP_MACOSX
mozilla::GetProcInfo(childPid, childId, type, origin,
aGeckoProcess->GetChildTask())
#else
mozilla::GetProcInfo(childPid, childId, type, origin)
#endif
);
});
auto ProcInfoResolver =
[domPromise, parentPid, parentInfo = aParentInfo](
const nsTArray<ProcInfo>& aChildrenInfo) {
mozilla::dom::ParentProcInfoDictionary procInfo;
// parent, basic info.
procInfo.mPid = parentPid;
procInfo.mFilename.Assign(parentInfo.filename);
procInfo.mType = mozilla::dom::WebIDLProcType::Browser;
procInfo.mVirtualMemorySize = parentInfo.virtualMemorySize;
procInfo.mResidentSetSize = parentInfo.residentSetSize;
procInfo.mCpuUser = parentInfo.cpuUser;
procInfo.mCpuKernel = parentInfo.cpuKernel;
// parent, threads info.
mozilla::dom::Sequence<mozilla::dom::ThreadInfoDictionary>
threads;
for (const ThreadInfo& entry : parentInfo.threads) {
ThreadInfoDictionary* thread =
threads.AppendElement(fallible);
if (NS_WARN_IF(!thread)) {
domPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
return;
}
thread->mCpuUser = entry.cpuUser;
thread->mCpuKernel = entry.cpuKernel;
thread->mTid = entry.tid;
thread->mName.Assign(entry.name);
}
procInfo.mThreads = std::move(threads);
mozilla::dom::Sequence<mozilla::dom::ChildProcInfoDictionary>
children;
for (const ProcInfo& info : aChildrenInfo) {
ChildProcInfoDictionary* childProcInfo =
children.AppendElement(fallible);
if (NS_WARN_IF(!childProcInfo)) {
domPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
return;
}
// Basic info.
childProcInfo->mChildID = info.childId;
childProcInfo->mType = ProcTypeToWebIDL(info.type);
childProcInfo->mOrigin = info.origin;
childProcInfo->mPid = info.pid;
childProcInfo->mFilename.Assign(info.filename);
childProcInfo->mVirtualMemorySize = info.virtualMemorySize;
childProcInfo->mResidentSetSize = info.residentSetSize;
childProcInfo->mCpuUser = info.cpuUser;
childProcInfo->mCpuKernel = info.cpuKernel;
// Threads info.
mozilla::dom::Sequence<mozilla::dom::ThreadInfoDictionary>
threads;
for (const ThreadInfo& entry : info.threads) {
ThreadInfoDictionary* thread =
threads.AppendElement(fallible);
if (NS_WARN_IF(!thread)) {
domPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
return;
}
thread->mCpuUser = entry.cpuUser;
thread->mCpuKernel = entry.cpuKernel;
thread->mTid = entry.tid;
thread->mName.Assign(entry.name);
}
childProcInfo->mThreads = std::move(threads);
}
procInfo.mChildren = std::move(children);
domPromise->MaybeResolve(procInfo);
}; // end of ProcInfoResolver
ProcInfoPromise::All(target, promises)
->Then(target, __func__, std::move(ProcInfoResolver),
[domPromise](const nsresult aResult) {
domPromise->MaybeReject(aResult);
}); // end of ProcInfoPromise::All
},
[domPromise](nsresult aRv) { domPromise->MaybeReject(aRv); });
MOZ_ASSERT(domPromise);
[domPromise](nsresult aRv) {
domPromise->MaybeReject(aRv);
}); // end of mozilla::GetProcInfo
// sending back the promise instance
return domPromise.forget();

Просмотреть файл

@ -238,16 +238,6 @@ class ContentParent final
const nsACString& aRemoteType, BrowsingContext* aBrowsingContext,
ContentParent* aOpenerContentParent);
/**
* Get all content parents.
*
* # Lifetime
*
* These pointers are ONLY valid for synchronous use from the main thread.
*
* Do NOT attempt to use them after the main thread has had a chance to handle
* messages or you could end up with dangling pointers.
*/
static void GetAll(nsTArray<ContentParent*>& aArray);
static void GetAllEvenIfDead(nsTArray<ContentParent*>& aArray);

Просмотреть файл

@ -180,8 +180,6 @@ class GeckoChildProcessHost : public ChildProcessHost,
// Iterates over all instances and calls aCallback with each one of them.
// This method will lock any addition/removal of new processes
// so you need to make sure the callback is as fast as possible.
//
// To reiterate: the callbacks are executed synchronously.
static void GetAll(const GeckoProcessCallback& aCallback);
friend class BaseProcessLauncher;

Просмотреть файл

@ -9,7 +9,6 @@
#include <base/process.h>
#include <stdint.h>
#include "mozilla/dom/ipc/IdType.h"
#include "mozilla/HashTable.h"
#include "mozilla/MozPromise.h"
namespace mozilla {
@ -84,117 +83,24 @@ struct ProcInfo {
CopyableTArray<ThreadInfo> threads;
};
typedef MozPromise<mozilla::HashMap<base::ProcessId, ProcInfo>, nsresult, true>
ProcInfoPromise;
typedef MozPromise<ProcInfo, nsresult, true> ProcInfoPromise;
/**
* Data we need to request process info (e.g. CPU usage, memory usage)
* from the operating system and populate the resulting `ProcInfo`.
/*
* GetProcInfo() uses a background thread to perform system calls.
*
* Note that this structure contains a mix of:
* - low-level handles that we need to request low-level process info
* (`aChildTask` on macOS, `aPid` on other platforms); and
* - high-level data that we already acquired while looking for
* `aPid`/`aChildTask` and that we will need further down the road.
* Depending on the platform, this call can be quite expensive and the
* promise may return after several ms.
*/
struct ProcInfoRequest {
ProcInfoRequest(base::ProcessId aPid, ProcType aProcessType,
const nsACString& aOrigin, uint32_t aChildId = 0
#ifdef XP_MACOSX
,
mach_port_t aChildTask = 0
#endif // XP_MACOSX
)
: pid(aPid),
processType(aProcessType),
origin(aOrigin),
childId(aChildId)
#ifdef XP_MACOSX
,
childTask(aChildTask)
#endif // XP_MACOSX
{
}
const base::ProcessId pid;
const ProcType processType;
const nsCString origin;
// If the process is a child, its child id, otherwise `0`.
const int32_t childId;
#ifdef XP_MACOSX
const mach_port_t childTask;
#endif // XP_MACOSX
};
/**
* Batch a request for low-level information on Gecko processes.
*
* # Request
*
* Argument `aRequests` is a list of processes, along with high-level data
* we have already obtained on them and that we need to populate the
* resulting array of `ProcInfo`.
*
* # Result
*
* This call succeeds (possibly with missing data, see below) unless we
* cannot allocate memory.
*
* # Performance
*
* - This call is always executed on a background thread.
* - This call does NOT wake up children processes.
* - This function is sometimes observably slow to resolve, in particular
* under Windows.
*
* # Error-handling and race conditions
*
* Requesting low-level information on a process and its threads is inherently
* subject to race conditions. Typically, if a process or a thread is killed
* while we're preparing to fetch information, we can easily end up with
* system/lib calls that return failures.
*
* For this reason, this API assumes that errors when placing a system/lib call
* are likely and normal. When some information cannot be obtained, the API will
* simply skip over said information.
*
* Note that due to different choices by OSes, the exact information we skip may
* vary across platforms. For instance, under Unix, failing to access the
* threads of a process will cause us to skip all data on the process, while
* under Windows, process information will be returned without thread
* information.
*/
RefPtr<ProcInfoPromise> GetProcInfo(nsTArray<ProcInfoRequest>&& aRequests);
/**
* Utility function: copy data from a `ProcInfo` and into either a
* `ParentProcInfoDictionary` or a `ChildProcInfoDictionary`.
*/
template <typename T>
nsresult CopySysProcInfoToDOM(const ProcInfo& source, T* dest) {
// Copy system info.
dest->mPid = source.pid;
dest->mFilename.Assign(source.filename);
dest->mVirtualMemorySize = source.virtualMemorySize;
dest->mResidentSetSize = source.residentSetSize;
dest->mCpuUser = source.cpuUser;
dest->mCpuKernel = source.cpuKernel;
// Copy thread info.
mozilla::dom::Sequence<mozilla::dom::ThreadInfoDictionary> threads;
for (const ThreadInfo& entry : source.threads) {
mozilla::dom::ThreadInfoDictionary* thread =
threads.AppendElement(fallible);
if (NS_WARN_IF(!thread)) {
return NS_ERROR_OUT_OF_MEMORY;
}
thread->mCpuUser = entry.cpuUser;
thread->mCpuKernel = entry.cpuKernel;
thread->mTid = entry.tid;
thread->mName.Assign(entry.name);
}
dest->mThreads = std::move(threads);
return NS_OK;
}
RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
const ProcType& processType,
const nsACString& origin,
mach_port_t aChildTask = MACH_PORT_NULL);
#else
RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
const ProcType& processType,
const nsACString& origin);
#endif
} // namespace mozilla
#endif // ProcInfo_h

Просмотреть файл

@ -9,7 +9,9 @@
namespace mozilla {
RefPtr<ProcInfoPromise> GetProcInfo(nsTArray<ProcInfoRequest>&& aRequests) {
RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
const ProcType& type,
const nsACString& origin) {
// Not implemented on Android.
return ProcInfoPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__);
}

Просмотреть файл

@ -5,7 +5,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ProcInfo.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "nsNetCID.h"
@ -20,9 +19,11 @@
namespace mozilla {
RefPtr<ProcInfoPromise> GetProcInfo(nsTArray<ProcInfoRequest>&& aRequests) {
RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId, const ProcType& type,
const nsACString& origin, mach_port_t aChildTask) {
auto holder = MakeUnique<MozPromiseHolder<ProcInfoPromise>>();
RefPtr<ProcInfoPromise> promise = holder->Ensure(__func__);
nsresult rv = NS_OK;
nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
@ -31,115 +32,93 @@ RefPtr<ProcInfoPromise> GetProcInfo(nsTArray<ProcInfoRequest>&& aRequests) {
return promise;
}
RefPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [holder = std::move(holder), requests = std::move(aRequests)]() {
HashMap<base::ProcessId, ProcInfo> gathered;
if (!gathered.reserve(requests.Length())) {
holder->Reject(NS_ERROR_OUT_OF_MEMORY, __func__);
return;
}
for (const auto& request : requests) {
ProcInfo info;
info.pid = request.pid;
info.childId = request.childId;
info.type = request.processType;
info.origin = std::move(request.origin);
struct proc_bsdinfo proc;
if ((unsigned long)proc_pidinfo(request.pid, PROC_PIDTBSDINFO, 0, &proc,
PROC_PIDTBSDINFO_SIZE) < PROC_PIDTBSDINFO_SIZE) {
// Can't read data for this proc.
// Probably either a sandboxing issue or a race condition, e.g.
// the process has been just been killed. Regardless, skip process.
continue;
}
// Ensure that the string is still alive when `ResolveGetProcInfo` is called.
nsCString originCopy(origin);
auto ResolveGetProcinfo = [holder = std::move(holder), pid, type,
originCopy = std::move(originCopy), childId, aChildTask]() {
ProcInfo info;
info.pid = pid;
info.childId = childId;
info.type = type;
info.origin = originCopy;
struct proc_bsdinfo proc;
if ((unsigned long)proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &proc, PROC_PIDTBSDINFO_SIZE) <
PROC_PIDTBSDINFO_SIZE) {
holder->Reject(NS_ERROR_FAILURE, __func__);
return;
}
struct proc_taskinfo pti;
if ((unsigned long)proc_pidinfo(request.pid, PROC_PIDTASKINFO, 0, &pti,
PROC_PIDTASKINFO_SIZE) < PROC_PIDTASKINFO_SIZE) {
continue;
}
struct proc_taskinfo pti;
if ((unsigned long)proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &pti, PROC_PIDTASKINFO_SIZE) <
PROC_PIDTASKINFO_SIZE) {
holder->Reject(NS_ERROR_FAILURE, __func__);
return;
}
// copying all the info to the ProcInfo struct
info.filename.AssignASCII(proc.pbi_name);
info.virtualMemorySize = pti.pti_virtual_size;
info.residentSetSize = pti.pti_resident_size;
info.cpuUser = pti.pti_total_user;
info.cpuKernel = pti.pti_total_system;
// copying all the info to the ProcInfo struct
info.filename.AssignASCII(proc.pbi_name);
info.virtualMemorySize = pti.pti_virtual_size;
info.residentSetSize = pti.pti_resident_size;
info.cpuUser = pti.pti_total_user;
info.cpuKernel = pti.pti_total_system;
// Now getting threads info
mach_port_t selectedTask;
// If we did not get a task from a child process, we use mach_task_self()
if (request.childTask == MACH_PORT_NULL) {
selectedTask = mach_task_self();
} else {
selectedTask = request.childTask;
}
// task_threads() gives us a snapshot of the process threads
// but those threads can go away. All the code below makes
// the assumption that thread_info() calls may fail, and
// these errors will be ignored.
thread_act_port_array_t threadList;
mach_msg_type_number_t threadCount;
kern_return_t kret = task_threads(selectedTask, &threadList, &threadCount);
if (kret != KERN_SUCCESS) {
// For some reason, we have no data on the threads for this process.
// Most likely reason is that we have just lost a race condition and
// the process is dead.
// Let's stop here and ignore the entire process.
continue;
}
// Now getting threads info
mach_port_t selectedTask;
// If we did not get a task from a child process, we use mach_task_self()
if (aChildTask == MACH_PORT_NULL) {
selectedTask = mach_task_self();
} else {
selectedTask = aChildTask;
}
// task_threads() gives us a snapshot of the process threads
// but those threads can go away. All the code below makes
// the assumption that thread_info() calls may fail, and
// these errors will be ignored.
thread_act_port_array_t threadList;
mach_msg_type_number_t threadCount;
kern_return_t kret = task_threads(selectedTask, &threadList, &threadCount);
if (kret != KERN_SUCCESS) {
holder->Resolve(info, __func__);
return;
}
auto guardThreadCount = MakeScopeExit([&] {
vm_deallocate(selectedTask, (vm_address_t)threadList, sizeof(thread_t) * threadCount);
});
mach_msg_type_number_t count;
mach_msg_type_number_t count;
for (mach_msg_type_number_t i = 0; i < threadCount; i++) {
// Basic thread info.
thread_extended_info_data_t threadInfoData;
count = THREAD_EXTENDED_INFO_COUNT;
kret =
thread_info(threadList[i], THREAD_EXTENDED_INFO, (thread_info_t)&threadInfoData, &count);
if (kret != KERN_SUCCESS) {
continue;
}
for (mach_msg_type_number_t i = 0; i < threadCount; i++) {
// Basic thread info.
thread_extended_info_data_t threadInfoData;
count = THREAD_EXTENDED_INFO_COUNT;
kret = thread_info(threadList[i], THREAD_EXTENDED_INFO, (thread_info_t)&threadInfoData,
&count);
if (kret != KERN_SUCCESS) {
continue;
}
// Getting the thread id.
thread_identifier_info identifierInfo;
count = THREAD_IDENTIFIER_INFO_COUNT;
kret = thread_info(threadList[i], THREAD_IDENTIFIER_INFO, (thread_info_t)&identifierInfo,
&count);
if (kret != KERN_SUCCESS) {
continue;
}
// Getting the thread id.
thread_identifier_info identifierInfo;
count = THREAD_IDENTIFIER_INFO_COUNT;
kret = thread_info(threadList[i], THREAD_IDENTIFIER_INFO,
(thread_info_t)&identifierInfo, &count);
if (kret != KERN_SUCCESS) {
continue;
}
// The two system calls were successful, let's add that thread
ThreadInfo* thread = info.threads.AppendElement(fallible);
if (!thread) {
holder->Reject(NS_ERROR_OUT_OF_MEMORY, __func__);
return;
}
thread->cpuUser = threadInfoData.pth_user_time;
thread->cpuKernel = threadInfoData.pth_system_time;
thread->name.AssignASCII(threadInfoData.pth_name);
thread->tid = identifierInfo.thread_id;
}
if (!gathered.put(request.pid, std::move(info))) {
holder->Reject(NS_ERROR_OUT_OF_MEMORY, __func__);
return;
}
}
// ... and we're done!
holder->Resolve(std::move(gathered), __func__);
});
// The two system calls were successful, let's add that thread
ThreadInfo thread;
thread.cpuUser = threadInfoData.pth_user_time;
thread.cpuKernel = threadInfoData.pth_system_time;
thread.name.AssignASCII(threadInfoData.pth_name);
thread.tid = identifierInfo.thread_id;
info.threads.AppendElement(thread);
}
holder->Resolve(info, __func__);
};
RefPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, std::move(ResolveGetProcinfo));
rv = target->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch the LoadDataRunnable.");
}
return promise;
}
} // namespace mozilla
}

Просмотреть файл

@ -209,7 +209,9 @@ class ThreadInfoReader final : public StatReader {
base::ProcessId mTid;
};
RefPtr<ProcInfoPromise> GetProcInfo(nsTArray<ProcInfoRequest>&& aRequests) {
RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
const ProcType& type,
const nsACString& origin) {
auto holder = MakeUnique<MozPromiseHolder<ProcInfoPromise>>();
RefPtr<ProcInfoPromise> promise = holder->Ensure(__func__);
nsresult rv = NS_OK;
@ -221,72 +223,56 @@ RefPtr<ProcInfoPromise> GetProcInfo(nsTArray<ProcInfoRequest>&& aRequests) {
return promise;
}
// Ensure that the string is still alive when the runnable is called.
nsCString originCopy(origin);
RefPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__,
[holder = std::move(holder), requests = std::move(aRequests)]() {
HashMap<base::ProcessId, ProcInfo> gathered;
if (!gathered.reserve(requests.Length())) {
holder->Reject(NS_ERROR_OUT_OF_MEMORY, __func__);
__func__, [holder = std::move(holder), pid, type,
originCopy = std::move(originCopy), childId]() {
// opening the stat file and reading its content
StatReader reader(pid);
ProcInfo info;
nsresult rv = reader.ParseProc(info);
if (NS_FAILED(rv)) {
holder->Reject(rv, __func__);
return;
}
for (const auto& request : requests) {
// opening the stat file and reading its content
StatReader reader(request.pid);
ProcInfo info;
nsresult rv = reader.ParseProc(info);
if (NS_FAILED(rv)) {
// Can't read data for this proc.
// Probably either a sandboxing issue or a race condition, e.g.
// the process has been just been killed. Regardless, skip process.
continue;
}
// Extra info
info.pid = request.pid;
info.childId = request.childId;
info.type = request.processType;
info.origin = request.origin;
// Extra info
info.pid = pid;
info.childId = childId;
info.type = type;
info.origin = originCopy;
// Let's look at the threads
nsCString taskPath;
taskPath.AppendPrintf("/proc/%u/task", request.pid);
nsAutoRef<DIR> dirHandle(opendir(taskPath.get()));
if (!dirHandle) {
// For some reason, we have no data on the threads for this process.
// Most likely reason is that we have just lost a race condition and
// the process is dead.
// Let's stop here and ignore the entire process.
continue;
}
// If we can't read some thread info, we ignore that thread.
dirent* entry;
while ((entry = readdir(dirHandle)) != nullptr) {
if (entry->d_name[0] == '.') {
continue;
}
// Threads have a stat file, like processes.
nsAutoCString entryName(entry->d_name);
int32_t tid = entryName.ToInteger(&rv);
if (NS_FAILED(rv)) {
continue;
}
ThreadInfoReader reader(request.pid, tid);
ThreadInfo threadInfo;
rv = reader.ParseThread(threadInfo);
if (NS_FAILED(rv)) {
continue;
}
info.threads.AppendElement(threadInfo);
}
if (!gathered.put(request.pid, std::move(info))) {
holder->Reject(NS_ERROR_OUT_OF_MEMORY, __func__);
return;
}
// Let's look at the threads
nsCString taskPath;
taskPath.AppendPrintf("/proc/%u/task", pid);
nsAutoRef<DIR> dirHandle(opendir(taskPath.get()));
if (!dirHandle) {
// No threads ? Let's stop here and ignore the problem.
holder->Resolve(info, __func__);
return;
}
// ... and we're done!
holder->Resolve(std::move(gathered), __func__);
// If we can't read some thread info, we ignore that thread.
dirent* entry;
while ((entry = readdir(dirHandle)) != nullptr) {
if (entry->d_name[0] == '.') {
continue;
}
// Threads have a stat file, like processes.
nsAutoCString entryName(entry->d_name);
int32_t tid = entryName.ToInteger(&rv);
if (NS_FAILED(rv)) {
continue;
}
ThreadInfoReader reader(pid, tid);
ThreadInfo threadInfo;
rv = reader.ParseThread(threadInfo);
if (NS_FAILED(rv)) {
continue;
}
info.threads.AppendElement(threadInfo);
}
holder->Resolve(info, __func__);
});
rv = target->Dispatch(r.forget(), NS_DISPATCH_NORMAL);

Просмотреть файл

@ -21,7 +21,63 @@ uint64_t ToNanoSeconds(const FILETIME& aFileTime) {
return usec.QuadPart * 100;
}
RefPtr<ProcInfoPromise> GetProcInfo(nsTArray<ProcInfoRequest>&& aRequests) {
void AppendThreads(ProcInfo* info) {
THREADENTRY32 te32;
// `GetThreadDescription` is available as of Windows 10.
// We attempt to import it dynamically, knowing that it
// may be `nullptr`.
auto getThreadDescription =
reinterpret_cast<GETTHREADDESCRIPTION>(::GetProcAddress(
::GetModuleHandleW(L"Kernel32.dll"), "GetThreadDescription"));
// Take a snapshot of all running threads, system-wide.
nsAutoHandle hThreadSnap(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));
if (!hThreadSnap) {
return;
}
te32.dwSize = sizeof(THREADENTRY32);
// Retrieve information about the first thread,
// and exit if unsuccessful
if (!Thread32First(hThreadSnap.get(), &te32)) {
return;
}
do {
if (te32.th32OwnerProcessID == info->pid) {
nsAutoHandle hThread(
OpenThread(THREAD_QUERY_INFORMATION, TRUE, te32.th32ThreadID));
if (!hThread) {
continue;
}
FILETIME createTime, exitTime, kernelTime, userTime;
if (!GetThreadTimes(hThread.get(), &createTime, &exitTime, &kernelTime,
&userTime)) {
continue;
}
ThreadInfo thread;
if (getThreadDescription) {
PWSTR threadName = nullptr;
if (getThreadDescription(hThread.get(), &threadName) && threadName) {
thread.name = threadName;
}
if (threadName) {
LocalFree(threadName);
}
}
thread.tid = te32.th32ThreadID;
thread.cpuKernel = ToNanoSeconds(kernelTime);
thread.cpuUser = ToNanoSeconds(userTime);
info->threads.AppendElement(thread);
}
} while (Thread32Next(hThreadSnap.get(), &te32));
}
RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
const ProcType& type,
const nsACString& origin) {
auto holder = MakeUnique<MozPromiseHolder<ProcInfoPromise>>();
RefPtr<ProcInfoPromise> promise = holder->Ensure(__func__);
@ -34,136 +90,50 @@ RefPtr<ProcInfoPromise> GetProcInfo(nsTArray<ProcInfoRequest>&& aRequests) {
return promise;
}
// Ensure that the string is still alive when `ResolveGetProcInfo` is called.
nsCString originCopy(origin);
RefPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__,
[holder = std::move(holder), requests = std::move(aRequests)]() -> void {
HashMap<base::ProcessId, ProcInfo> gathered;
if (!gathered.reserve(requests.Length())) {
holder->Reject(NS_ERROR_OUT_OF_MEMORY, __func__);
[holder = std::move(holder), originCopy = std::move(originCopy), pid,
type, childId]() -> void {
nsAutoHandle handle(OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
if (!handle) {
holder->Reject(NS_ERROR_FAILURE, __func__);
return;
}
// ---- Copying data on processes (minus threads).
for (const auto& request : requests) {
nsAutoHandle handle(OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, request.pid));
if (!handle) {
// Ignore process, it may have died.
continue;
}
wchar_t filename[MAX_PATH];
if (GetProcessImageFileNameW(handle.get(), filename, MAX_PATH) == 0) {
// Ignore process, it may have died.
continue;
}
FILETIME createTime, exitTime, kernelTime, userTime;
if (!GetProcessTimes(handle.get(), &createTime, &exitTime,
&kernelTime, &userTime)) {
// Ignore process, it may have died.
continue;
}
PROCESS_MEMORY_COUNTERS memoryCounters;
if (!GetProcessMemoryInfo(handle.get(),
(PPROCESS_MEMORY_COUNTERS)&memoryCounters,
sizeof(memoryCounters))) {
// Ignore process, it may have died.
continue;
}
// Assumption: values of `pid` are distinct between processes,
// regardless of any race condition we might have stumbled upon. Even
// if it somehow could happen, in the worst case scenario, we might
// end up overwriting one process info and we might end up with too
// many threads attached to a process, as the data is not crucial, we
// do not need to defend against that (unlikely) scenario.
ProcInfo info;
info.pid = request.pid;
info.childId = request.childId;
info.type = request.processType;
info.origin = request.origin;
info.filename.Assign(filename);
info.cpuKernel = ToNanoSeconds(kernelTime);
info.cpuUser = ToNanoSeconds(userTime);
info.residentSetSize = memoryCounters.WorkingSetSize;
info.virtualMemorySize = memoryCounters.PagefileUsage;
if (!gathered.put(request.pid, std::move(info))) {
holder->Reject(NS_ERROR_OUT_OF_MEMORY, __func__);
return;
}
}
// ---- Add thread data to already-copied processes.
// First, we need to capture a snapshot of all the threads on this
// system.
nsAutoHandle hThreadSnap(CreateToolhelp32Snapshot(
/* dwFlags */ TH32CS_SNAPTHREAD, /* ignored */ 0));
if (!hThreadSnap) {
holder->Reject(NS_ERROR_UNEXPECTED, __func__);
wchar_t filename[MAX_PATH];
if (GetProcessImageFileNameW(handle.get(), filename, MAX_PATH) == 0) {
holder->Reject(NS_ERROR_FAILURE, __func__);
return;
}
// `GetThreadDescription` is available as of Windows 10.
// We attempt to import it dynamically, knowing that it
// may be `nullptr`.
auto getThreadDescription =
reinterpret_cast<GETTHREADDESCRIPTION>(::GetProcAddress(
::GetModuleHandleW(L"Kernel32.dll"), "GetThreadDescription"));
THREADENTRY32 te32;
te32.dwSize = sizeof(THREADENTRY32);
// Now, walk through the threads.
for (auto success = Thread32First(hThreadSnap.get(), &te32); success;
success = Thread32Next(hThreadSnap.get(), &te32)) {
auto processLookup = gathered.lookup(te32.th32OwnerProcessID);
if (!processLookup) {
// Not one of the processes we're interested in.
continue;
}
ThreadInfo* threadInfo =
processLookup->value().threads.AppendElement(fallible);
if (!threadInfo) {
holder->Reject(NS_ERROR_OUT_OF_MEMORY, __func__);
return;
}
nsAutoHandle hThread(
OpenThread(/* dwDesiredAccess = */ THREAD_QUERY_INFORMATION,
/* bInheritHandle = */ FALSE,
/* dwThreadId = */ te32.th32ThreadID));
if (!hThread) {
// Cannot open thread. Not sure why, but let's attempt to find data
// on other threads.
continue;
}
FILETIME createTime, exitTime, kernelTime, userTime;
if (!GetThreadTimes(hThread.get(), &createTime, &exitTime,
&kernelTime, &userTime)) {
continue;
}
if (getThreadDescription) {
PWSTR threadName = nullptr;
if (getThreadDescription(hThread.get(), &threadName) &&
threadName) {
threadInfo->name = threadName;
}
if (threadName) {
LocalFree(threadName);
}
}
threadInfo->tid = te32.th32ThreadID;
threadInfo->cpuKernel = ToNanoSeconds(kernelTime);
threadInfo->cpuUser = ToNanoSeconds(userTime);
FILETIME createTime, exitTime, kernelTime, userTime;
if (!GetProcessTimes(handle.get(), &createTime, &exitTime, &kernelTime,
&userTime)) {
holder->Reject(NS_ERROR_FAILURE, __func__);
return;
}
// ----- We're ready to return.
holder->Resolve(std::move(gathered), __func__);
PROCESS_MEMORY_COUNTERS memoryCounters;
if (!GetProcessMemoryInfo(handle.get(),
(PPROCESS_MEMORY_COUNTERS)&memoryCounters,
sizeof(memoryCounters))) {
holder->Reject(NS_ERROR_FAILURE, __func__);
return;
}
ProcInfo info;
info.pid = pid;
info.childId = childId;
info.type = type;
info.origin = originCopy;
info.filename.Assign(filename);
info.cpuKernel = ToNanoSeconds(kernelTime);
info.cpuUser = ToNanoSeconds(userTime);
info.residentSetSize = memoryCounters.WorkingSetSize;
info.virtualMemorySize = memoryCounters.PagefileUsage;
AppendThreads(&info);
holder->Resolve(info, __func__);
});
rv = target->Dispatch(r.forget(), NS_DISPATCH_NORMAL);