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) { |