| Index: third_party/WebKit/Source/core/events/EventDispatcher.cpp
|
| diff --git a/third_party/WebKit/Source/core/events/EventDispatcher.cpp b/third_party/WebKit/Source/core/events/EventDispatcher.cpp
|
| index d069a1c9915fdbd64d5f4580fc562e9c1d86c480..d591daba67ead31ad67e418a438e24971c1fe545 100644
|
| --- a/third_party/WebKit/Source/core/events/EventDispatcher.cpp
|
| +++ b/third_party/WebKit/Source/core/events/EventDispatcher.cpp
|
| @@ -26,9 +26,14 @@
|
| #include "config.h"
|
| #include "core/events/EventDispatcher.h"
|
|
|
| +#include <iostream>
|
| +#include <string>
|
| +
|
| +#include "content/renderer/greenweb_latency_tracking.h"
|
| #include "core/dom/ContainerNode.h"
|
| #include "core/dom/Document.h"
|
| #include "core/dom/Element.h"
|
| +#include "core/dom/NodeComputedStyle.h"
|
| #include "core/events/EventDispatchMediator.h"
|
| #include "core/events/MouseEvent.h"
|
| #include "core/events/ScopedEventQueue.h"
|
| @@ -36,10 +41,15 @@
|
| #include "core/frame/FrameView.h"
|
| #include "core/frame/LocalDOMWindow.h"
|
| #include "core/inspector/InspectorTraceEvents.h"
|
| +#include "core/style/ComputedStyle.h"
|
| #include "platform/EventDispatchForbiddenScope.h"
|
| #include "platform/TraceEvent.h"
|
| #include "wtf/RefPtr.h"
|
|
|
| +char g_qos_type = 'u';
|
| +int g_qos_target = 0;
|
| +bool g_ebs_enabled = false;
|
| +
|
| namespace blink {
|
|
|
| bool EventDispatcher::dispatchEvent(Node& node, PassRefPtrWillBeRawPtr<EventDispatchMediator> mediator)
|
| @@ -102,6 +112,320 @@ void EventDispatcher::dispatchSimulatedClick(Node& node, Event* underlyingEvent,
|
| nodesDispatchingSimulatedClicks->remove(&node);
|
| }
|
|
|
| +bool init_ebs(bool *mode)
|
| +{
|
| + FILE* arg_file;
|
| + arg_file = fopen("/data/local/tmp/chrome-shell-command-line", "r");
|
| + if (!arg_file) {
|
| + dbg_sched_fprintf(stdout, "[EBS] arg file open failed\n");
|
| + return false;
|
| + }
|
| + dbg_sched_fprintf(stdout, "[EBS] arg file opened\n");
|
| +
|
| + char arg_list[100];
|
| + fgets(arg_list, 100, arg_file);
|
| + fclose(arg_file);
|
| +
|
| + if (arg_list[13] == '-'
|
| + && arg_list[14] == '-'
|
| + && arg_list[15] == 'e'
|
| + && arg_list[16] == 'b'
|
| + && arg_list[17] == 's'
|
| + && arg_list[18] == '-'
|
| + && arg_list[19] == 'p')
|
| + {
|
| + if (arg_list[20] == 'i') {
|
| + *mode = true;
|
| + dbg_sched_fprintf(stdout, "[EBS] ebs enabled. Using Pi\n");
|
| + return true;
|
| + }
|
| + if (arg_list[20] == 'u') {
|
| + *mode = false;
|
| + dbg_sched_fprintf(stdout, "[EBS] ebs enabled. Using Pu\n");
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +void set_freq(uint32 freq)
|
| +{
|
| + FILE* cpufreq = NULL;
|
| +
|
| + if (!cpufreq) {
|
| + cpufreq = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed", "w");
|
| + if (!cpufreq)
|
| + dbg_sched_fprintf(stdout, "[EBS] file open failed\n");
|
| + else
|
| + dbg_sched_fprintf(stdout, "[EBS] freq file open succeeded\n");
|
| + }
|
| +
|
| + if (cpufreq) {
|
| + int bytes = fprintf(cpufreq, "%u", freq);
|
| + if ((bytes != 6) && (bytes != 7)) {
|
| + dbg_sched_fprintf(stdout, "[EBS] cpufreq not set\n");
|
| + } else {
|
| + dbg_sched_fprintf(stdout, "[EBS] freq set to %d\n", freq);
|
| +#ifdef EBS_DEBUG_TRACE_EVENT
|
| + TRACE_EVENT2("devtools.timeline", "EBS:Schedule",
|
| + "log", "freq set",
|
| + "freq", freq);
|
| +#endif
|
| + }
|
| + fclose(cpufreq);
|
| + }
|
| +}
|
| +
|
| +void schedule(uint32 last_state, int64 last_latency, uint32 *next_state, uint32 *next_freq, int qos_target, FreqLatencyMap *freq_map, int64 dom_node_id)
|
| +{
|
| + // FSM state explanation:
|
| + // 0xFF - Calibrating on big core, 1.6G
|
| + // 0x00 - Calibrating on big core, 0.8G
|
| + // 0x01 - Calibrating on little core, 0.6G
|
| + // 0x02 - Calibrating on little core, 0.4G
|
| + // 0xN - Running on core with frequency N/10 GHz (N = 3~16)
|
| +
|
| + // First time we run an event on this node. Run with 1.6G.
|
| + // ODroid doesn't allow setting all 4 cores to 1.8G.
|
| + if (last_latency == 0) {
|
| + *next_state = 0xFF;
|
| + *next_freq = 1600000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: Now calibrate on 1.6G\n", last_state, *next_state);
|
| + return;
|
| + }
|
| +
|
| + // Second time we run an event on this node. Run with 0.8G.
|
| + if (last_state == 0xFF) {
|
| + *next_state = 0;
|
| + *next_freq = 800000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: Now calibrate on 0.8G\n", last_state, *next_state);
|
| + } else if (last_state == 0) {
|
| + // Finished the two profiling runs on the big core.
|
| + int64 time_8 = (*freq_map)[800000];
|
| + int64 time_16 = (*freq_map)[1600000];
|
| +
|
| + if (qos_target <= time_16) {
|
| + // Have to provision the highest conf.
|
| + *next_state = 16;
|
| + *next_freq = 1600000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: T16 > Target. Have to use the highest big\n", last_state, *next_state);
|
| + } else if (qos_target >= time_8) {
|
| + // Try the little core
|
| + *next_state = 1;
|
| + *next_freq = 600000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: T8 < Target. Now calibrate on 0.6G\n", last_state, *next_state);
|
| + } else {
|
| + if (time_8 <= time_16) {
|
| + // Recalibrate on the big core
|
| + *next_state = 0xFF;
|
| + *next_freq = 1600000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: T8 < T16. Recalibrate on big\n", last_state, *next_state);
|
| + } else {
|
| + double beta = (time_8 - time_16) * 1.6 * 1000000;
|
| + double alpha = time_8 - beta / 800000;
|
| + *next_freq = (int)(beta / (qos_target - alpha) + 1) / 100000 * 100000;
|
| + *next_state = *next_freq / 100000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: On big, predicted by model\n", last_state, *next_state);
|
| + }
|
| + }
|
| + } else if (last_state == 1) {
|
| + // First profiling run on the little core.
|
| + *next_state = 2;
|
| + // Never uses 300 MHz, beucase it leads to more energy than 400 MHz
|
| + *next_freq = 400000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: Now calibrate on 0.4G\n", last_state, *next_state);
|
| + } else if (last_state == 2) {
|
| + // Finished the two profiling runs on the little core.
|
| + int64 time_4 = (*freq_map)[400000];
|
| + int64 time_6 = (*freq_map)[600000];
|
| +
|
| + if (qos_target <= time_6) {
|
| + // Recalibrate on the big core
|
| + *next_state = 0xFF;
|
| + *next_freq = 1600000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: T6 > Target. Recalibrate on big\n", last_state, *next_state);
|
| + } else if (qos_target >= time_4) {
|
| + // It's safe to provision the lowest on the little core
|
| + *next_state = 4;
|
| + *next_freq = 400000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: T4 < Target. Safe to use the lowest little\n", last_state, *next_state);
|
| + } else {
|
| + if (time_4 <= time_6) {
|
| + // Recalibrate on the little core
|
| + *next_state = 1;
|
| + *next_freq = 600000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: T4 < T6. Recalibrate on little\n", last_state, *next_state);
|
| + } else {
|
| + double beta = (time_4 - time_6) * 1.2 * 1000000;
|
| + double alpha = time_4 - beta / 400000;
|
| + *next_freq = (int)(beta / (qos_target - alpha) + 1) / 100000 * 100000;
|
| + *next_state = *next_freq / 100000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: On little, predicted by model\n", last_state, *next_state);
|
| + }
|
| + }
|
| + } else {
|
| + // Fine tune phase, depending on the last_latency.
|
| + static std::map<int64, int> underpred;
|
| + static std::map<int64, int> overpred;
|
| +
|
| + if (last_latency < qos_target * 0.8) {
|
| + // Overpredicting
|
| + if (last_state == 4) {
|
| + // Already lowest. Nothing we can do.
|
| + *next_state = last_state;
|
| + *next_freq = (*next_state) * 100000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: Keep the last prediction\n", last_state, *next_state);
|
| + return;
|
| + }
|
| +
|
| + underpred[dom_node_id] = 0;
|
| + overpred[dom_node_id]++;
|
| +
|
| + if (overpred[dom_node_id] <= 2) {
|
| + if (last_state == 4)
|
| + *next_state = 4;
|
| + else if (last_state == 8)
|
| + *next_state = 6;
|
| + else
|
| + *next_state = last_state - 1;
|
| + *next_freq = (*next_state) * 100000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: Overpredict\n", last_state, *next_state);
|
| + } else if (overpred[dom_node_id] <= 4) {
|
| + if (last_state <= 5)
|
| + *next_state = 4;
|
| + else if (last_state == 9)
|
| + *next_state = 6;
|
| + else
|
| + *next_state = last_state - 2;
|
| + *next_freq = (*next_state) * 100000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: Too many overpredict\n", last_state, *next_state);
|
| + } else {
|
| + overpred[dom_node_id] = 0;
|
| + *next_state = 0xFF;
|
| + *next_freq = 1600000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: Way too many overpredict\n", last_state, *next_state);
|
| + }
|
| + } else if (last_latency > qos_target * 0.9) {
|
| + // Underpredicting
|
| + if (last_state == 16) {
|
| + // Already highest. Nothing we can do.
|
| + *next_state = last_state;
|
| + *next_freq = (*next_state) * 100000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: Keep the last prediction\n", last_state, *next_state);
|
| + return;
|
| + }
|
| +
|
| + overpred[dom_node_id] = 0;
|
| + underpred[dom_node_id]++;
|
| +
|
| + if (underpred[dom_node_id] <= 2) {
|
| + if (last_state == 16)
|
| + *next_state = 16;
|
| + else if (last_state == 6)
|
| + *next_state = 8;
|
| + else
|
| + *next_state = last_state + 1;
|
| + *next_freq = (*next_state) * 100000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: Underpredict\n", last_state, *next_state);
|
| + } else if (underpred[dom_node_id] <= 4) {
|
| + if (last_state >= 15)
|
| + *next_state = 16;
|
| + else if (last_state == 5)
|
| + *next_state = 8;
|
| + else
|
| + *next_state = last_state + 2;
|
| + *next_freq = (*next_state) * 100000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: Too many underpredict\n", last_state, *next_state);
|
| + } else {
|
| + underpred[dom_node_id] = 0;
|
| + *next_state = 0xFF;
|
| + *next_freq = 1600000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: Way too many underpredict\n", last_state, *next_state);
|
| + }
|
| + } else {
|
| + overpred[dom_node_id] = 0;
|
| + underpred[dom_node_id] = 0;
|
| + *next_state = last_state;
|
| + *next_freq = (*next_state) * 100000;
|
| + dbg_sched_fprintf(stdout, "[EBS] state %d --> %d: Keep the last prediction\n", last_state, *next_state);
|
| + }
|
| + }
|
| +
|
| +#ifdef EBS_DEBUG_TRACE_EVENT
|
| + TRACE_EVENT2("devtools.timeline", "EBS:Schedule",
|
| + "last_state", last_state,
|
| + "next_state", *next_state);
|
| +#endif
|
| +}
|
| +
|
| +int isEventSupported(String event_name)
|
| +{
|
| + if (event_name == "click")
|
| + return 1;
|
| + else if(event_name == "touchstart")
|
| + return 2;
|
| + else if(event_name == "touchend")
|
| + return 3;
|
| + else if(event_name == "scroll")
|
| + return 4;
|
| + else if(event_name == "touchmove")
|
| + return 5;
|
| + return 0;
|
| +}
|
| +
|
| +void EventDispatcher::retriveQoSInfo(bool mode, int event)
|
| +{
|
| + int pi, pu, type;
|
| +
|
| + // TODO: handle cases where multiple events register callbacks on one node
|
| + switch (event) {
|
| + case 1: // click
|
| + pi = m_node->computedStyle()->onclickVpi();
|
| + pu = m_node->computedStyle()->onclickVpu();
|
| + type = m_node->computedStyle()->onclickType();
|
| + break;
|
| + case 2: // touchstart
|
| + pi = m_node->computedStyle()->ontouchstartVpi();
|
| + pu = m_node->computedStyle()->ontouchstartVpu();
|
| + type = m_node->computedStyle()->ontouchstartType();
|
| + break;
|
| + case 3: // touchend
|
| + pi = m_node->computedStyle()->ontouchendVpi();
|
| + pu = m_node->computedStyle()->ontouchendVpu();
|
| + type = m_node->computedStyle()->ontouchendType();
|
| + break;
|
| + case 4: // scroll
|
| + pi = m_node->computedStyle()->onscrollVpi();
|
| + pu = m_node->computedStyle()->onscrollVpu();
|
| + type = m_node->computedStyle()->onscrollType();
|
| + break;
|
| + case 5: // touchmove
|
| + pi = m_node->computedStyle()->ontouchmoveVpi();
|
| + pu = m_node->computedStyle()->ontouchmoveVpu();
|
| + type = m_node->computedStyle()->ontouchmoveType();
|
| + break;
|
| + default: // Should never reach here
|
| + dbg_sched_fprintf(stdout, "[EBS] Wrong event id. Should never reach here!\n");
|
| + ASSERT_NOT_REACHED();
|
| + return;
|
| + }
|
| +
|
| + g_qos_target = mode ? pi : pu;
|
| +
|
| + std::string QoSType;
|
| + if (type == 0)
|
| + QoSType = "single";
|
| + else
|
| + QoSType = "continuous";
|
| + g_qos_type = QoSType[0];
|
| +
|
| + dbg_sched_cout("Node " << m_node->nodeName().utf8().data() <<
|
| + " on" << m_event->type().string().utf8().data() <<
|
| + ": (" << QoSType << ")" <<
|
| + " [" << pi << ", " << pu << "]");
|
| +}
|
| +
|
| bool EventDispatcher::dispatch()
|
| {
|
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("blink.debug"), "EventDispatcher::dispatch");
|
| @@ -120,6 +444,97 @@ bool EventDispatcher::dispatch()
|
| ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden());
|
| ASSERT(m_event->target());
|
| TRACE_EVENT1("devtools.timeline", "EventDispatch", "data", InspectorEventDispatchEvent::data(*m_event));
|
| +
|
| + static bool init = false;
|
| + static bool mode = true; // true-Pi, false-Pu
|
| +
|
| + if (!init) {
|
| + init = true;
|
| + g_ebs_enabled = init_ebs(&mode);
|
| + }
|
| +
|
| + String event_name = m_event->type().string();
|
| + int event_id = isEventSupported(event_name);
|
| + if (event_id) {
|
| + int64 dom_node_id = reinterpret_cast<int64>(m_node.get());
|
| + InputLatencyMap[InputLatencyID].dom_node = dom_node_id;
|
| +
|
| + if (g_ebs_enabled && m_node->computedStyle()) {
|
| + retriveQoSInfo(mode, event_id);
|
| +
|
| + // Deal with the single QoS type here. The GPU process deals with the continuous case.
|
| + if (g_qos_type == 's') {
|
| + // Memoize the freq to latency mapping for each DOM node.
|
| + // Used for constructing/calibrating the model and prediction.
|
| + // Data structure: std::map<int64, std::map<uint32, int64>>
|
| + static NodeFreqLatencyMap EBSCoreMap;
|
| + // Memoize the latency, state, and freq for each DOM node
|
| + // when it was executed last time.
|
| + static std::map<int64, int64> last_latency;
|
| + static std::map<int64, uint32> last_state;
|
| + static std::map<int64, uint32> last_freq;
|
| +
|
| + uint32 last_state_t = last_state[dom_node_id];
|
| + uint32 last_freq_t = last_freq[dom_node_id];
|
| +
|
| + if (DOMLatencyMap.find(dom_node_id) != DOMLatencyMap.end()) {
|
| + // Updating last_latency of this DOM node.
|
| + // This could not be done eagerly before it's actually executed.
|
| + last_latency[dom_node_id] = DOMLatencyMap[dom_node_id].latency;
|
| + EBSCoreMap[dom_node_id][last_freq_t] = last_latency[dom_node_id];
|
| + }
|
| +#ifdef EBS_DEBUG_TRACE_EVENT
|
| + TRACE_EVENT2("devtools.timeline", "GreenWeb:EventDispatch",
|
| + "trace_id", InputLatencyID,
|
| + "last_latency", DOMLatencyMap[dom_node_id].latency);
|
| +#endif
|
| + dbg_sched_cout("[EBS] trace_id " << InputLatencyID <<
|
| + " dom_node " << dom_node_id <<
|
| + " last_freq " << last_freq_t <<
|
| + " last_latency " << DOMLatencyMap[dom_node_id].latency);
|
| +
|
| + uint32 next_state;
|
| + uint32 next_freq;
|
| + int64 last_latency_t = last_latency[dom_node_id];
|
| +
|
| + schedule(last_state_t,
|
| + last_latency_t,
|
| + &next_state,
|
| + &next_freq,
|
| + g_qos_target,
|
| + &EBSCoreMap[dom_node_id],
|
| + dom_node_id);
|
| + set_freq(next_freq);
|
| +
|
| + // Early updating last_state and last_freq of this DOM node.
|
| + // Next time we see this DOM node, its last_state and last_freq
|
| + // is the next_state and next_freq set in the previous run.
|
| + // Note that the last_latency of a DOM node can only be set
|
| + // when it's seen the next time -- it has to be executed before
|
| + // we know it's latency!
|
| + last_state[dom_node_id] = next_state;
|
| + last_freq[dom_node_id] = next_freq;
|
| + }
|
| + } else { // EBS not enabled
|
| +#ifdef EBS_DEBUG_OS
|
| + std::cout << "[OS] dom_node " << dom_node_id << " last_latency " << DOMLatencyMap[dom_node_id].latency << std::endl;
|
| + for (int i = 4; i <= 16; i++) {
|
| + if (i == 7)
|
| + continue;
|
| + std::cout << i * 100000 << " " << DOMLatencyMap[dom_node_id].FreqStatMap[i * 100000] << std::endl;
|
| + }
|
| + fprintf(stdout, " \n");
|
| +#endif
|
| + }
|
| + }
|
| +
|
| + static int counter_s = 0;
|
| + if ((counter_s != 4) && !strcmp(m_event->type().string().utf8().data(), "readystatechange")) {
|
| + if (++counter_s == 4)
|
| + fprintf(stdout, "PageStart\n");
|
| + }
|
| + fflush(stdout);
|
| +
|
| void* preDispatchEventHandlerResult;
|
| if (dispatchEventPreProcess(preDispatchEventHandlerResult) == ContinueDispatching) {
|
| if (dispatchEventAtCapturing() == ContinueDispatching) {
|
|
|