From c562fee6cd9bb413d9c885bdffa32654de105cf3 Mon Sep 17 00:00:00 2001 From: Cosmin Sabou Date: Sun, 19 Jul 2020 05:54:07 +0300 Subject: [PATCH] Backed out 2 changesets (bug 1652000) for causing Bug 1653749. Backed out changeset 69dbf38f4071 (bug 1652000) Backed out changeset 0a6cb920a247 (bug 1652000) --- dom/base/ChromeUtils.cpp | 395 ++++++++++++++++--------------- dom/ipc/ContentParent.h | 10 - ipc/glue/GeckoChildProcessHost.h | 2 - widget/ProcInfo.h | 122 ++-------- widget/android/ProcInfo.cpp | 4 +- widget/cocoa/ProcInfo.mm | 179 +++++++------- widget/gtk/ProcInfo.cpp | 108 ++++----- widget/windows/ProcInfo.cpp | 216 ++++++++--------- 8 files changed, 438 insertions(+), 598 deletions(-) diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp index 4eb46b9a1646..baca12862dbd 100644 --- a/dom/base/ChromeUtils.cpp +++ b/dom/base/ChromeUtils.cpp @@ -781,7 +781,7 @@ already_AddRefed 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 global = do_QueryInterface(aGlobal.GetAsSupports()); MOZ_ASSERT(global); RefPtr domPromise = Promise::Create(global, aRv); @@ -790,204 +790,213 @@ already_AddRefed 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 contentParents; - ContentParent::GetAll(contentParents); - - // Prepare our background request. - // We reserve one more slot for the browser process itself. - nsTArray 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 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& 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 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 contentParents; + ContentParent::GetAll(contentParents); + nsTArray> 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 - 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& 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 + 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 + 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 + 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(); diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index e0e3a6b7d351..61f946eeab66 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -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& aArray); static void GetAllEvenIfDead(nsTArray& aArray); diff --git a/ipc/glue/GeckoChildProcessHost.h b/ipc/glue/GeckoChildProcessHost.h index 5d8b2c00cec7..32cb36277ec3 100644 --- a/ipc/glue/GeckoChildProcessHost.h +++ b/ipc/glue/GeckoChildProcessHost.h @@ -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; diff --git a/widget/ProcInfo.h b/widget/ProcInfo.h index bfd1a66c1a50..0076603adfe4 100644 --- a/widget/ProcInfo.h +++ b/widget/ProcInfo.h @@ -9,7 +9,6 @@ #include #include #include "mozilla/dom/ipc/IdType.h" -#include "mozilla/HashTable.h" #include "mozilla/MozPromise.h" namespace mozilla { @@ -84,117 +83,24 @@ struct ProcInfo { CopyableTArray threads; }; -typedef MozPromise, nsresult, true> - ProcInfoPromise; +typedef MozPromise 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 GetProcInfo(nsTArray&& aRequests); - -/** - * Utility function: copy data from a `ProcInfo` and into either a - * `ParentProcInfoDictionary` or a `ChildProcInfoDictionary`. - */ -template -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 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 GetProcInfo(base::ProcessId pid, int32_t childId, + const ProcType& processType, + const nsACString& origin, + mach_port_t aChildTask = MACH_PORT_NULL); +#else +RefPtr GetProcInfo(base::ProcessId pid, int32_t childId, + const ProcType& processType, + const nsACString& origin); +#endif } // namespace mozilla #endif // ProcInfo_h diff --git a/widget/android/ProcInfo.cpp b/widget/android/ProcInfo.cpp index 5a9089238547..0bcdcb625cce 100644 --- a/widget/android/ProcInfo.cpp +++ b/widget/android/ProcInfo.cpp @@ -9,7 +9,9 @@ namespace mozilla { -RefPtr GetProcInfo(nsTArray&& aRequests) { +RefPtr 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__); } diff --git a/widget/cocoa/ProcInfo.mm b/widget/cocoa/ProcInfo.mm index 03142cdf8e2b..6dc233dd0ba5 100644 --- a/widget/cocoa/ProcInfo.mm +++ b/widget/cocoa/ProcInfo.mm @@ -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 GetProcInfo(nsTArray&& aRequests) { +RefPtr GetProcInfo(base::ProcessId pid, int32_t childId, const ProcType& type, + const nsACString& origin, mach_port_t aChildTask) { auto holder = MakeUnique>(); RefPtr promise = holder->Ensure(__func__); + nsresult rv = NS_OK; nsCOMPtr target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv); if (NS_FAILED(rv)) { @@ -31,115 +32,93 @@ RefPtr GetProcInfo(nsTArray&& aRequests) { return promise; } - RefPtr r = NS_NewRunnableFunction( - __func__, [holder = std::move(holder), requests = std::move(aRequests)]() { - HashMap 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 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 +} diff --git a/widget/gtk/ProcInfo.cpp b/widget/gtk/ProcInfo.cpp index e833b63dadfd..857f45245fc7 100644 --- a/widget/gtk/ProcInfo.cpp +++ b/widget/gtk/ProcInfo.cpp @@ -209,7 +209,9 @@ class ThreadInfoReader final : public StatReader { base::ProcessId mTid; }; -RefPtr GetProcInfo(nsTArray&& aRequests) { +RefPtr GetProcInfo(base::ProcessId pid, int32_t childId, + const ProcType& type, + const nsACString& origin) { auto holder = MakeUnique>(); RefPtr promise = holder->Ensure(__func__); nsresult rv = NS_OK; @@ -221,72 +223,56 @@ RefPtr GetProcInfo(nsTArray&& aRequests) { return promise; } + // Ensure that the string is still alive when the runnable is called. + nsCString originCopy(origin); RefPtr r = NS_NewRunnableFunction( - __func__, - [holder = std::move(holder), requests = std::move(aRequests)]() { - HashMap 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 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 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); diff --git a/widget/windows/ProcInfo.cpp b/widget/windows/ProcInfo.cpp index cff46ecaadd7..1050bdd7064b 100644 --- a/widget/windows/ProcInfo.cpp +++ b/widget/windows/ProcInfo.cpp @@ -21,7 +21,63 @@ uint64_t ToNanoSeconds(const FILETIME& aFileTime) { return usec.QuadPart * 100; } -RefPtr GetProcInfo(nsTArray&& 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(::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 GetProcInfo(base::ProcessId pid, int32_t childId, + const ProcType& type, + const nsACString& origin) { auto holder = MakeUnique>(); RefPtr promise = holder->Ensure(__func__); @@ -34,136 +90,50 @@ RefPtr GetProcInfo(nsTArray&& aRequests) { return promise; } + // Ensure that the string is still alive when `ResolveGetProcInfo` is called. + nsCString originCopy(origin); RefPtr r = NS_NewRunnableFunction( __func__, - [holder = std::move(holder), requests = std::move(aRequests)]() -> void { - HashMap 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(::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);