Changeset 266388 in webkit


Ignore:
Timestamp:
Sep 1, 2020 1:55:06 AM (4 years ago)
Author:
Carlos Garcia Campos
Message:

[Linux] Web Inspector: show per thread cpu usage
https://bugs.webkit.org/show_bug.cgi?id=215883

Reviewed by Adrian Perez de Castro.

Source/JavaScriptCore:

Remove platform specific getter machThread() and add thread() to return the Thread instead. The caller knows how
to get the machThread or id from a Thread.

  • runtime/SamplingProfiler.cpp:

(JSC::SamplingProfiler::reportTopBytecodes):
(JSC::SamplingProfiler::machThread): Deleted.

  • runtime/SamplingProfiler.h:

(JSC::SamplingProfiler::thread):

Source/WebCore:

Get per thread CPU usage and information to fill ResourceUsageData in Linux.

  • page/ResourceUsageThread.h:
  • page/cocoa/ResourceUsageThreadCocoa.mm:

(WebCore::ResourceUsageThread::platformSaveStateBeforeStarting): Update to new API in SamplingProfiler.

  • page/linux/ResourceUsageThreadLinux.cpp:

(WebCore::ResourceUsageThread::platformSaveStateBeforeStarting): Initialize m_samplingProfilerThreadID.
(WebCore::threadInfoMap):
(WebCore::threadCPUUsage):
(WebCore::collectCPUUsage):
(WebCore::ResourceUsageThread::platformCollectCPUData):
(WebCore::cpuUsage): Deleted.

Source/WTF:

Add API to get the thread ID in Linux platform.

  • wtf/Threading.cpp:

(WTF::Thread::initializeInThread):

  • wtf/Threading.h:

(WTF::Thread::id const):

  • wtf/ThreadingPrimitives.h:
  • wtf/posix/ThreadingPOSIX.cpp:

(WTF::Thread::currentID):

Location:
trunk/Source
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r266359 r266388  
     12020-09-01  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        [Linux] Web Inspector: show per thread cpu usage
     4        https://bugs.webkit.org/show_bug.cgi?id=215883
     5
     6        Reviewed by Adrian Perez de Castro.
     7
     8        Remove platform specific getter machThread() and add thread() to return the Thread instead. The caller knows how
     9        to get the machThread or id from a Thread.
     10
     11        * runtime/SamplingProfiler.cpp:
     12        (JSC::SamplingProfiler::reportTopBytecodes):
     13        (JSC::SamplingProfiler::machThread): Deleted.
     14        * runtime/SamplingProfiler.h:
     15        (JSC::SamplingProfiler::thread):
     16
    1172020-08-31  Yusuke Suzuki  <ysuzuki@apple.com>
    218
  • trunk/Source/JavaScriptCore/runtime/SamplingProfiler.cpp

    r264736 r266388  
    11421142}
    11431143
    1144 #if OS(DARWIN)
    1145 mach_port_t SamplingProfiler::machThread()
    1146 {
    1147     if (!m_thread)
    1148         return MACH_PORT_NULL;
    1149 
    1150     return m_thread->machThread();
    1151 }
    1152 #endif
     1144Thread* SamplingProfiler::thread() const
     1145{
     1146    return m_thread.get();
     1147}
    11531148
    11541149} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/SamplingProfiler.h

    r261233 r266388  
    194194    JS_EXPORT_PRIVATE void reportTopBytecodes(PrintStream&);
    195195
    196 #if OS(DARWIN)
    197     JS_EXPORT_PRIVATE mach_port_t machThread();
    198 #endif
     196    JS_EXPORT_PRIVATE Thread* thread() const;
    199197
    200198private:
  • trunk/Source/WTF/ChangeLog

    r266354 r266388  
     12020-09-01  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        [Linux] Web Inspector: show per thread cpu usage
     4        https://bugs.webkit.org/show_bug.cgi?id=215883
     5
     6        Reviewed by Adrian Perez de Castro.
     7
     8        Add API to get the thread ID in Linux platform.
     9
     10        * wtf/Threading.cpp:
     11        (WTF::Thread::initializeInThread):
     12        * wtf/Threading.h:
     13        (WTF::Thread::id const):
     14        * wtf/ThreadingPrimitives.h:
     15        * wtf/posix/ThreadingPOSIX.cpp:
     16        (WTF::Thread::currentID):
     17
    1182020-08-31  Alex Christensen  <achristensen@webkit.org>
    219
  • trunk/Source/WTF/wtf/Threading.cpp

    r265735 r266388  
    147147    }
    148148#endif
     149
     150#if OS(LINUX)
     151    m_id = currentID();
     152#endif
    149153}
    150154
  • trunk/Source/WTF/wtf/Threading.h

    r263635 r266388  
    158158
    159159#if USE(PTHREADS)
     160#if OS(LINUX)
     161    WTF_EXPORT_PRIVATE static ThreadIdentifier currentID();
     162    ThreadIdentifier id() const { return m_id; }
     163#endif
    160164    WTF_EXPORT_PRIVATE bool signal(int signalNumber);
    161165#endif
     
    330334    mach_port_t m_platformThread { MACH_PORT_NULL };
    331335#elif USE(PTHREADS)
     336#if OS(LINUX)
     337    ThreadIdentifier m_id { 0 };
     338#endif
    332339    PlatformRegisters* m_platformRegisters { nullptr };
    333340    unsigned m_suspendCount { 0 };
  • trunk/Source/WTF/wtf/ThreadingPrimitives.h

    r253730 r266388  
    5858using PlatformCondition = pthread_cond_t;
    5959using ThreadSpecificKey = pthread_key_t;
     60#if OS(LINUX)
     61using ThreadIdentifier = pid_t;
     62#endif
    6063#elif OS(WINDOWS)
    6164using ThreadIdentifier = uint32_t;
  • trunk/Source/WTF/wtf/posix/ThreadingPOSIX.cpp

    r265383 r266388  
    6969#endif
    7070
     71#if OS(LINUX)
     72#include <sys/syscall.h>
     73#endif
     74
    7175namespace WTF {
    7276
     
    187191#endif
    188192}
     193
     194#if OS(LINUX)
     195ThreadIdentifier Thread::currentID()
     196{
     197    return static_cast<ThreadIdentifier>(syscall(SYS_gettid));
     198}
     199#endif
    189200
    190201void Thread::initializeCurrentThreadEvenIfNonWTFCreated()
  • trunk/Source/WebCore/ChangeLog

    r266387 r266388  
     12020-09-01  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        [Linux] Web Inspector: show per thread cpu usage
     4        https://bugs.webkit.org/show_bug.cgi?id=215883
     5
     6        Reviewed by Adrian Perez de Castro.
     7
     8        Get per thread CPU usage and information to fill ResourceUsageData in Linux.
     9
     10        * page/ResourceUsageThread.h:
     11        * page/cocoa/ResourceUsageThreadCocoa.mm:
     12        (WebCore::ResourceUsageThread::platformSaveStateBeforeStarting): Update to new API in SamplingProfiler.
     13        * page/linux/ResourceUsageThreadLinux.cpp:
     14        (WebCore::ResourceUsageThread::platformSaveStateBeforeStarting): Initialize m_samplingProfilerThreadID.
     15        (WebCore::threadInfoMap):
     16        (WebCore::threadCPUUsage):
     17        (WebCore::collectCPUUsage):
     18        (WebCore::ResourceUsageThread::platformCollectCPUData):
     19        (WebCore::cpuUsage): Deleted.
     20
    1212020-08-31  Myles C. Maxfield  <mmaxfield@apple.com>
    222
  • trunk/Source/WebCore/page/ResourceUsageThread.h

    r247815 r266388  
    8989    JSC::VM* m_vm { nullptr };
    9090
    91 #if ENABLE(SAMPLING_PROFILER) && OS(DARWIN)
     91#if ENABLE(SAMPLING_PROFILER)
     92#if OS(DARWIN)
    9293    mach_port_t m_samplingProfilerMachThread { MACH_PORT_NULL };
     94#elif OS(LINUX)
     95    pid_t m_samplingProfilerThreadID { 0 };
     96#endif
    9397#endif
    9498
  • trunk/Source/WebCore/page/cocoa/ResourceUsageThreadCocoa.mm

    r260682 r266388  
    137137{
    138138#if ENABLE(SAMPLING_PROFILER)
    139     m_samplingProfilerMachThread = m_vm->samplingProfiler() ? m_vm->samplingProfiler()->machThread() : MACH_PORT_NULL;
     139    m_samplingProfilerMachThread = MACH_PORT_NULL;
     140
     141    if (auto* profiler = m_vm->samplingProfiler()) {
     142        if (auto* thread = profiler->thread())
     143            m_samplingProfilerMachThread = thread->machThread();
     144    }
    140145#endif
    141146}
  • trunk/Source/WebCore/page/linux/ResourceUsageThreadLinux.cpp

    r241739 r266388  
    11/*
    2  * Copyright (C) 2017 Igalia S.L.
     2 * Copyright (C) 2017, 2020 Igalia S.L.
    33 * Copyright (C) 2018 Apple Inc. All rights reserved.
    44 *
     
    3030#if ENABLE(RESOURCE_USAGE) && OS(LINUX)
    3131
     32#include "WorkerThread.h"
    3233#include <JavaScriptCore/GCActivityCallback.h>
     34#include <JavaScriptCore/SamplingProfiler.h>
    3335#include <JavaScriptCore/VM.h>
    3436#include <errno.h>
     
    3941#include <sys/types.h>
    4042#include <unistd.h>
     43#include <wtf/Threading.h>
    4144#include <wtf/linux/CurrentProcessMemoryStatus.h>
    4245
     
    9194}
    9295
    93 static float cpuUsage()
    94 {
    95     float period = cpuPeriod();
    96     if (!period)
    97         return -1;
    98 
    99     int fd = open("/proc/self/stat", O_RDONLY);
     96void ResourceUsageThread::platformSaveStateBeforeStarting()
     97{
     98#if ENABLE(SAMPLING_PROFILER)
     99    m_samplingProfilerThreadID = 0;
     100
     101    if (auto* profiler = m_vm->samplingProfiler()) {
     102        if (auto* thread = profiler->thread())
     103            m_samplingProfilerThreadID = thread->id();
     104    }
     105#endif
     106}
     107
     108struct ThreadInfo {
     109    Optional<String> name;
     110    Optional<float> cpuUsage;
     111    unsigned long long previousUtime { 0 };
     112    unsigned long long previousStime { 0 };
     113};
     114
     115static HashMap<pid_t, ThreadInfo>& threadInfoMap()
     116{
     117    static LazyNeverDestroyed<HashMap<pid_t, ThreadInfo>> map;
     118    static std::once_flag flag;
     119    std::call_once(flag, [&] {
     120        map.construct();
     121    });
     122    return map;
     123}
     124
     125static bool threadCPUUsage(pid_t id, float period, ThreadInfo& info)
     126{
     127    String path = makeString("/proc/self/task/", id, "/stat");
     128    int fd = open(path.utf8().data(), O_RDONLY);
    100129    if (fd < 0)
    101         return -1;
     130        return false;
    102131
    103132    static const ssize_t maxBufferLength = BUFSIZ - 1;
     
    111140            if (errno != EINTR) {
    112141                close(fd);
    113                 return -1;
     142                return false;
    114143            }
    115144            continue;
     
    124153    buffer[totalBytesRead] = '\0';
    125154
    126     // Skip pid and process name.
    127     char* position = strrchr(buffer, ')');
     155    // Skip tid and name.
     156    char* position = strchr(buffer, ')');
    128157    if (!position)
    129         return -1;
     158        return false;
     159
     160    if (!info.name) {
     161        char* name = strchr(buffer, '(');
     162        if (!name)
     163            return false;
     164        name++;
     165        info.name = String::fromUTF8(name, position - name);
     166    }
    130167
    131168    // Move after state.
     
    140177    }
    141178
    142     static unsigned long long previousUtime = 0;
    143     static unsigned long long previousStime = 0;
    144179    unsigned long long utime = strtoull(position, &position, 10);
    145180    unsigned long long stime = strtoull(position, &position, 10);
    146     float usage = (utime + stime - (previousUtime + previousStime)) / period * 100.0;
    147     previousUtime = utime;
    148     previousStime = stime;
    149 
    150     return clampTo<float>(usage, 0, 100);
    151 }
    152 
    153 void ResourceUsageThread::platformSaveStateBeforeStarting()
    154 {
     181    float usage = (utime + stime - (info.previousUtime + info.previousStime)) / period * 100.0;
     182    info.previousUtime = utime;
     183    info.previousStime = stime;
     184
     185    info.cpuUsage = clampTo<float>(usage, 0, 100);
     186    return true;
     187}
     188
     189static void collectCPUUsage(float period)
     190{
     191    DIR* dir = opendir("/proc/self/task");
     192    if (!dir) {
     193        threadInfoMap().clear();
     194        return;
     195    }
     196
     197    HashSet<pid_t> previousTasks;
     198    for (const auto& key : threadInfoMap().keys())
     199        previousTasks.add(key);
     200
     201    struct dirent* dp;
     202    while ((dp = readdir(dir))) {
     203        String name = String::fromUTF8(dp->d_name);
     204        if (name == "." || name == "..")
     205            continue;
     206
     207        bool ok;
     208        pid_t id = name.toIntStrict(&ok);
     209        if (!ok)
     210            continue;
     211
     212        auto& info = threadInfoMap().add(id, ThreadInfo()).iterator->value;
     213        if (!threadCPUUsage(id, period, info))
     214            threadInfoMap().remove(id);
     215
     216        previousTasks.remove(id);
     217    }
     218    closedir(dir);
     219
     220    threadInfoMap().removeIf([&](auto& entry)  {
     221        return previousTasks.contains(entry.key);
     222    });
    155223}
    156224
    157225void ResourceUsageThread::platformCollectCPUData(JSC::VM*, ResourceUsageData& data)
    158226{
    159     data.cpu = cpuUsage();
    160 
    161     // FIXME: Exclude the ResourceUsage thread.
    162     // FIXME: Exclude the SamplingProfiler thread.
    163     // FIXME: Classify usage per thread.
    164     data.cpuExcludingDebuggerThreads = data.cpu;
     227    float period = cpuPeriod();
     228    if (!period) {
     229        data.cpu = 0;
     230        data.cpuExcludingDebuggerThreads = 0;
     231        return;
     232    }
     233
     234    collectCPUUsage(period);
     235
     236    pid_t resourceUsageThreadID = Thread::currentID();
     237
     238    HashSet<pid_t> knownWebKitThreads;
     239    {
     240        auto locker = holdLock(Thread::allThreadsMutex());
     241        for (auto* thread : Thread::allThreads(locker)) {
     242            if (auto id = thread->id())
     243                knownWebKitThreads.add(id);
     244        }
     245    }
     246
     247    HashMap<pid_t, String> knownWorkerThreads;
     248    {
     249        LockHolder lock(WorkerThread::workerThreadsMutex());
     250        for (auto* thread : WorkerThread::workerThreads(lock)) {
     251            // Ignore worker threads that have not been fully started yet.
     252            if (!thread->thread())
     253                continue;
     254
     255            if (auto id = thread->thread()->id())
     256                knownWorkerThreads.set(id, thread->identifier().isolatedCopy());
     257        }
     258    }
     259
     260    auto isDebuggerThread = [&](pid_t id) -> bool {
     261        if (id == resourceUsageThreadID)
     262            return true;
     263#if ENABLE(SAMPLING_PROFILER)
     264        if (id == m_samplingProfilerThreadID)
     265            return true;
     266#endif
     267        return false;
     268    };
     269
     270    auto isWebKitThread = [&](pid_t id, const String& name) -> bool {
     271        if (knownWebKitThreads.contains(id))
     272            return true;
     273
     274        // The bmalloc scavenger thread is below WTF. Detect it by its name.
     275        if (name == "BMScavenger")
     276            return true;
     277
     278        return false;
     279    };
     280
     281    for (const auto& it : threadInfoMap()) {
     282        if (!it.value.cpuUsage)
     283            continue;
     284
     285        float cpuUsage = it.value.cpuUsage.value();
     286        pid_t id = it.key;
     287        data.cpu += cpuUsage;
     288        if (isDebuggerThread(id))
     289            continue;
     290
     291        data.cpuExcludingDebuggerThreads += cpuUsage;
     292
     293        if (getpid() == id)
     294            data.cpuThreads.append(ThreadCPUInfo { "Main Thread"_s, { }, cpuUsage, ThreadCPUInfo::Type::Main});
     295        else {
     296            String threadIdentifier = knownWorkerThreads.get(id);
     297            bool isWorkerThread = !threadIdentifier.isEmpty();
     298            String name = it.value.name.valueOr(emptyString());
     299            ThreadCPUInfo::Type type = (isWorkerThread || isWebKitThread(id, name)) ? ThreadCPUInfo::Type::WebKit : ThreadCPUInfo::Type::Unknown;
     300            data.cpuThreads.append(ThreadCPUInfo { name, threadIdentifier, cpuUsage, type });
     301        }
     302    }
    165303}
    166304
Note: See TracChangeset for help on using the changeset viewer.