Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(490)

Side by Side Diff: components/browser_watcher/system_session_analyzer_win.cc

Issue 2715903003: Bound the impact of system instability on chrome instability. (Closed)
Patch Set: record start timestamp, analysis metric, make log scrape lazy Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/browser_watcher/system_session_analyzer_win.h"
6
7 #include <windows.h>
8 #include <winevt.h>
9
10 #include <utility>
11
12 #include "base/time/time.h"
13
14 namespace browser_watcher {
15
16 namespace {
17
18 // The name of the log channel to query.
19 const wchar_t kChannelName[] = L"System";
20
21 const uint16_t kIdSessionStart = 6005U;
22 const uint16_t kIdSessionEnd = 6006U;
23 const uint16_t kIdSessionEndUnclean = 6008U;
24
25 // An XPATH expression to query for events marking system start or shutdown. The
26 // query looks at the following events:
27 // - id 6005: "The Event log service was started."
28 // - id 6006: "The Event log service was stopped."
29 // - id 6008: "The previous system shutdown at <time> on <date> was
30 // unexpected."
31 // This query is expected to retrieve exactly one event for each startup (id
32 // 6005) and one event for each shutdown (either 6006 or 6008).
33 const wchar_t kSessionEventsQuery[] =
34 L"*[System[Provider[@Name='eventlog']"
35 L" and (EventID=6005 or EventID=6006 or EventID=6008)]]";
36
37 // XPath expressions to attributes of interest.
38 const wchar_t kEventIdPath[] = L"Event/System/EventID";
39 const wchar_t kEventTimePath[] = L"Event/System/TimeCreated/@SystemTime";
40
41 // The timeout to use for calls to ::EvtNext.
42 const uint32_t kTimeoutMs = 5000;
43
44 struct EvtHandleCloser {
45 using pointer = EVT_HANDLE;
46 void operator()(EVT_HANDLE handle) const {
47 if (handle)
48 ::EvtClose(handle);
49 }
50 };
51
52 using EvtHandle = std::unique_ptr<EVT_HANDLE, EvtHandleCloser>;
53
54 // Create a render context (i.e. specify attributes of interest).
55 EvtHandle CreateRenderContext() {
56 LPCWSTR value_paths[] = {kEventIdPath, kEventTimePath};
57 DWORD value_cnt = 2U;
58
59 EVT_HANDLE context = NULL;
60 context =
61 ::EvtCreateRenderContext(value_cnt, value_paths, EvtRenderContextValues);
62 if (!context)
63 PLOG(ERROR) << "Failed to create render context.";
64
65 return EvtHandle(context);
66 }
67
68 bool GetEventInfo(EVT_HANDLE context,
69 EVT_HANDLE event,
70 SystemSessionEventFetcher::EventInfo* info) {
71 DCHECK(context);
72 DCHECK(event);
73 DCHECK(info);
74
75 std::vector<EVT_VARIANT> buffer(2U);
76 DWORD buffer_size = buffer.size() * sizeof(EVT_VARIANT);
77 DWORD buffer_used = 0U;
78 DWORD value_cnt = 0U;
79
80 if (!::EvtRender(context, event, EvtRenderEventValues, buffer_size,
81 buffer.data(), &buffer_used, &value_cnt)) {
82 PLOG(ERROR) << "Failed to render the event.";
83 return false;
84 }
85
86 if ((buffer[0].Type != EvtVarTypeUInt16) ||
87 (buffer[1].Type != EvtVarTypeFileTime)) {
88 // The types are not what we expect.
89 return false;
90 }
91
92 info->event_id = buffer[0].UInt16Val;
93
94 ULONGLONG time = buffer[1].FileTimeVal;
95 FILETIME ft;
96 ft.dwLowDateTime = static_cast<DWORD>(time & 0xFFFFFFFF);
97
98 ft.dwHighDateTime = static_cast<DWORD>(time >> 32);
99 info->event_time = base::Time::FromFileTime(ft);
100
101 return true;
102 }
103
104 } // namespace
105
106 SystemSessionEventFetcher::SystemSessionEventFetcher(size_t session_cnt)
107 : session_cnt_(session_cnt) {}
108
109 bool SystemSessionEventFetcher::FetchEvents(
110 std::vector<EventInfo>* event_infos) const {
111 DCHECK(event_infos);
112
113 // Query for the events. Note: requesting events from newest to oldest.
114 EVT_HANDLE query_raw =
115 ::EvtQuery(nullptr, kChannelName, kSessionEventsQuery,
116 EvtQueryChannelPath | EvtQueryReverseDirection);
117 if (!query_raw) {
118 PLOG(ERROR) << "Event query failed.";
119 return false;
120 }
121 EvtHandle query(query_raw);
122
123 // Retrieve events: 2 events per session, plus the current session's start.
124 size_t desired_event_cnt = 2 * session_cnt_ + 1;
125 std::vector<EVT_HANDLE> events_raw(desired_event_cnt, NULL);
126 DWORD event_cnt = 0U;
127 BOOL success = ::EvtNext(query.get(), desired_event_cnt, events_raw.data(),
128 kTimeoutMs, 0, &event_cnt);
129 if (!success) {
130 // Ensure we capture the last error immediately. Note: returning below to
131 // ensure potential handle cleanup.
132 PLOG(ERROR) << "Failed to retrieve events.";
133 }
134
135 // Ensure handles get closed. The MSDN sample seems to imply handles may need
136 // to be closed event in if EvtNext failed.
137 std::vector<EvtHandle> events(desired_event_cnt);
138 for (size_t i = 0; i < event_cnt; ++i)
139 events[i].reset(events_raw[i]);
140
141 if (!success)
142 return false;
143
144 // Extract information from the events.
145 EvtHandle render_context = CreateRenderContext();
146 if (!render_context.get())
147 return false;
148
149 std::vector<EventInfo> event_infos_tmp;
150 event_infos_tmp.reserve(event_cnt);
151
152 EventInfo info = {};
153 for (size_t i = 0; i < event_cnt; ++i) {
154 if (!GetEventInfo(render_context.get(), events[i].get(), &info))
155 return false;
156 event_infos_tmp.push_back(info);
157 }
158
159 event_infos->swap(event_infos_tmp);
160 return true;
161 }
162
163 SystemSessionAnalyzer::SystemSessionAnalyzer(
164 std::unique_ptr<SystemSessionEventFetcher> fetcher)
165 : event_fetcher_(std::move(fetcher)) {}
166
167 SystemSessionAnalyzer::Status SystemSessionAnalyzer::IsSessionUnclean(
168 base::Time timestamp) {
169 if (!initialized_)
170 Initialize();
171 if (!init_success_)
172 return FAILED;
173 if (timestamp < coverage_start_)
174 return OUTSIDE_RANGE;
175
176 // Get the first session starting after the timestamp.
177 std::map<base::Time, base::TimeDelta>::const_iterator it =
178 unclean_sessions_.upper_bound(timestamp);
179 if (it == unclean_sessions_.begin())
180 return CLEAN; // No prior unclean session.
181
182 // Get the previous session and see if it encompasses the timestamp.
183 --it;
184 bool is_spanned = (timestamp - it->first) <= it->second;
185 return is_spanned ? UNCLEAN : CLEAN;
186 }
187
188 void SystemSessionAnalyzer::Initialize() {
189 initialized_ = true;
190
191 if (!event_fetcher_)
192 return;
193 std::vector<SystemSessionEventFetcher::EventInfo> events;
194 if (!event_fetcher_->FetchEvents(&events))
195 return;
196
197 // Validate the number and ordering of events (newest to oldest).
198 size_t event_cnt = events.size();
199 if ((event_cnt % 2) == 0)
200 return; // Even number is unexpected.
201 if (event_cnt < 3)
202 return; // Not enough data for a single session.
203 for (size_t i = 1; i < event_cnt; ++i) {
204 if (events[i - 1].event_time < events[i].event_time)
205 return;
206 }
207
208 std::map<base::Time, base::TimeDelta> unclean_sessions;
209 size_t start = 2U;
210 size_t end = 1U;
211 for (; start < event_cnt && end < event_cnt; start += 2, end += 2) {
212 uint16_t start_id = events[start].event_id;
213 uint16_t end_id = events[end].event_id;
214 if (start_id != kIdSessionStart)
215 return;
216 if (end_id != kIdSessionEnd && end_id != kIdSessionEndUnclean)
217 return;
218
219 if (end_id == kIdSessionEnd)
220 continue;
221
222 unclean_sessions.insert(
223 std::make_pair(events[start].event_time,
224 events[end].event_time - events[start].event_time));
225 }
226
227 unclean_sessions_.swap(unclean_sessions);
228 coverage_start_ = events[event_cnt - 1].event_time;
229 init_success_ = true;
230 }
231
232 } // namespace browser_watcher
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698