OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/feedback/feedback_data.h" | 5 #include "components/feedback/feedback_data.h" |
6 | 6 |
| 7 #include "base/bind.h" |
7 #include "base/file_util.h" | 8 #include "base/file_util.h" |
8 #include "base/json/json_string_value_serializer.h" | 9 #include "base/json/json_string_value_serializer.h" |
9 #include "base/memory/ref_counted_memory.h" | 10 #include "base/memory/ref_counted_memory.h" |
10 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
11 #include "base/strings/utf_string_conversions.h" | 12 #include "base/strings/utf_string_conversions.h" |
12 #include "base/values.h" | 13 #include "base/values.h" |
13 #include "components/feedback/feedback_util.h" | 14 #include "components/feedback/feedback_util.h" |
14 #include "components/feedback/tracing_manager.h" | 15 #include "components/feedback/tracing_manager.h" |
15 #include "content/public/browser/browser_thread.h" | 16 #include "content/public/browser/browser_thread.h" |
16 | 17 |
17 using content::BrowserThread; | 18 using content::BrowserThread; |
18 | 19 |
19 namespace feedback { | 20 namespace feedback { |
20 namespace { | 21 namespace { |
21 | 22 |
22 const char kMultilineIndicatorString[] = "<multiline>\n"; | |
23 const char kMultilineStartString[] = "---------- START ----------\n"; | |
24 const char kMultilineEndString[] = "---------- END ----------\n\n"; | |
25 | |
26 const size_t kFeedbackMaxLength = 4 * 1024; | |
27 const size_t kFeedbackMaxLineCount = 40; | |
28 | |
29 const char kTraceFilename[] = "tracing.zip\n"; | 23 const char kTraceFilename[] = "tracing.zip\n"; |
30 const char kPerformanceCategoryTag[] = "Performance"; | 24 const char kPerformanceCategoryTag[] = "Performance"; |
31 | 25 |
32 const char kZipExt[] = ".zip"; | |
33 | |
34 const base::FilePath::CharType kLogsFilename[] = | |
35 FILE_PATH_LITERAL("system_logs.txt"); | |
36 const base::FilePath::CharType kHistogramsFilename[] = | 26 const base::FilePath::CharType kHistogramsFilename[] = |
37 FILE_PATH_LITERAL("histograms.txt"); | 27 FILE_PATH_LITERAL("histograms.txt"); |
38 | 28 |
39 // Converts the system logs into a string that we can compress and send | 29 const char kHistogramsAttachmentName[] = "histograms.zip"; |
40 // with the report. This method only converts those logs that we want in | |
41 // the compressed zip file sent with the report, hence it ignores any logs | |
42 // below the size threshold of what we want compressed. | |
43 std::string LogsToString(const FeedbackData::SystemLogsMap& sys_info) { | |
44 std::string syslogs_string; | |
45 for (FeedbackData::SystemLogsMap::const_iterator it = sys_info.begin(); | |
46 it != sys_info.end(); ++it) { | |
47 std::string key = it->first; | |
48 std::string value = it->second; | |
49 | |
50 if (FeedbackData::BelowCompressionThreshold(value)) | |
51 continue; | |
52 | |
53 base::TrimString(key, "\n ", &key); | |
54 base::TrimString(value, "\n ", &value); | |
55 | |
56 if (value.find("\n") != std::string::npos) { | |
57 syslogs_string.append( | |
58 key + "=" + kMultilineIndicatorString + | |
59 kMultilineStartString + | |
60 value + "\n" + | |
61 kMultilineEndString); | |
62 } else { | |
63 syslogs_string.append(key + "=" + value + "\n"); | |
64 } | |
65 } | |
66 return syslogs_string; | |
67 } | |
68 | |
69 void ZipFile(const base::FilePath& filename, | |
70 const std::string& data, std::string* compressed_data) { | |
71 if (!feedback_util::ZipString(filename, data, compressed_data)) | |
72 compressed_data->clear(); | |
73 } | |
74 | |
75 void ZipLogs(const FeedbackData::SystemLogsMap& sys_info, | |
76 std::string* compressed_logs) { | |
77 DCHECK(compressed_logs); | |
78 std::string logs_string = LogsToString(sys_info); | |
79 if (logs_string.empty() || | |
80 !feedback_util::ZipString( | |
81 base::FilePath(kLogsFilename), logs_string, compressed_logs)) { | |
82 compressed_logs->clear(); | |
83 } | |
84 } | |
85 | |
86 void ZipHistograms(const std::string& histograms, | |
87 std::string* compressed_histograms) { | |
88 DCHECK(compressed_histograms); | |
89 if (histograms.empty() || | |
90 !feedback_util::ZipString( | |
91 base::FilePath(kHistogramsFilename), | |
92 histograms, | |
93 compressed_histograms)) { | |
94 compressed_histograms->clear(); | |
95 } | |
96 } | |
97 | 30 |
98 } // namespace | 31 } // namespace |
99 | 32 |
100 // static | 33 FeedbackData::FeedbackData() |
101 bool FeedbackData::BelowCompressionThreshold(const std::string& content) { | 34 : send_report_(base::Bind(&feedback_util::SendReport)), context_(NULL), |
102 if (content.length() > kFeedbackMaxLength) | 35 trace_id_(0), pending_op_count_(1), report_sent_(false) {} |
103 return false; | |
104 const size_t line_count = std::count(content.begin(), content.end(), '\n'); | |
105 if (line_count > kFeedbackMaxLineCount) | |
106 return false; | |
107 return true; | |
108 } | |
109 | |
110 FeedbackData::FeedbackData() : context_(NULL), | |
111 trace_id_(0), | |
112 feedback_page_data_complete_(false), | |
113 syslogs_compression_complete_(false), | |
114 histograms_compression_complete_(false), | |
115 attached_file_compression_complete_(false), | |
116 report_sent_(false) { | |
117 } | |
118 | 36 |
119 FeedbackData::~FeedbackData() { | 37 FeedbackData::~FeedbackData() { |
120 } | 38 } |
121 | 39 |
122 void FeedbackData::OnFeedbackPageDataComplete() { | 40 void FeedbackData::OnFeedbackPageDataComplete() { |
123 feedback_page_data_complete_ = true; | 41 pending_op_count_--; |
124 SendReport(); | 42 SendReport(); |
125 } | 43 } |
126 | 44 |
127 void FeedbackData::SetAndCompressSystemInfo( | 45 void FeedbackData::SetAndCompressSystemInfo( |
128 scoped_ptr<FeedbackData::SystemLogsMap> sys_info) { | 46 scoped_ptr<FeedbackData::SystemLogsMap> sys_info) { |
129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 47 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
130 | 48 |
131 if (trace_id_ != 0) { | 49 if (trace_id_ != 0) { |
132 TracingManager* manager = TracingManager::Get(); | 50 TracingManager* manager = TracingManager::Get(); |
| 51 ++pending_op_count_; |
133 if (!manager || | 52 if (!manager || |
134 !manager->GetTraceData( | 53 !manager->GetTraceData( |
135 trace_id_, | 54 trace_id_, |
136 base::Bind(&FeedbackData::OnGetTraceData, this, trace_id_))) { | 55 base::Bind(&FeedbackData::OnGetTraceData, this, trace_id_))) { |
| 56 pending_op_count_--; |
137 trace_id_ = 0; | 57 trace_id_ = 0; |
138 } | 58 } |
139 } | 59 } |
140 | 60 |
141 sys_info_ = sys_info.Pass(); | 61 AddLogs(sys_info.Pass()); |
142 if (sys_info_.get()) { | 62 if (sys_info.get()) { |
143 std::string* compressed_logs_ptr = new std::string; | 63 ++pending_op_count_; |
144 scoped_ptr<std::string> compressed_logs(compressed_logs_ptr); | |
145 BrowserThread::PostBlockingPoolTaskAndReply( | 64 BrowserThread::PostBlockingPoolTaskAndReply( |
146 FROM_HERE, | 65 FROM_HERE, |
147 base::Bind(&ZipLogs, | 66 base::Bind(&FeedbackCommon::CompressLogs, this), |
148 *sys_info_, | 67 base::Bind(&FeedbackData::OnCompressComplete, this)); |
149 compressed_logs_ptr), | |
150 base::Bind(&FeedbackData::OnCompressLogsComplete, | |
151 this, | |
152 base::Passed(&compressed_logs))); | |
153 } | 68 } |
154 } | 69 } |
155 | 70 |
156 void FeedbackData::SetAndCompressHistograms( | 71 void FeedbackData::SetAndCompressHistograms( |
157 scoped_ptr<std::string> histograms) { | 72 scoped_ptr<std::string> histograms) { |
158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
159 | 74 |
160 histograms_ = histograms.Pass(); | 75 if (!histograms.get()) |
161 if (histograms_.get()) { | 76 return; |
162 std::string* compressed_histograms_ptr = new std::string; | 77 ++pending_op_count_; |
163 scoped_ptr<std::string> compressed_histograms(compressed_histograms_ptr); | 78 BrowserThread::PostBlockingPoolTaskAndReply( |
164 BrowserThread::PostBlockingPoolTaskAndReply( | 79 FROM_HERE, |
165 FROM_HERE, | 80 base::Bind(&FeedbackCommon::CompressFile, |
166 base::Bind(&ZipHistograms, | 81 this, |
167 *histograms_, | 82 base::FilePath(kHistogramsFilename), |
168 compressed_histograms_ptr), | 83 kHistogramsAttachmentName, |
169 base::Bind(&FeedbackData::OnCompressHistogramsComplete, | 84 base::Passed(&histograms)), |
170 this, | 85 base::Bind(&FeedbackData::OnCompressComplete, this)); |
171 base::Passed(&compressed_histograms))); | |
172 } | |
173 } | 86 } |
174 | 87 |
175 void FeedbackData::AttachAndCompressFileData( | 88 void FeedbackData::AttachAndCompressFileData( |
176 scoped_ptr<std::string> attached_filedata) { | 89 scoped_ptr<std::string> attached_filedata) { |
177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
178 | 91 |
179 attached_filedata_ = attached_filedata.Pass(); | 92 if (!attached_filedata.get() || attached_filedata->empty()) |
180 | 93 return; |
181 if (!attached_filename_.empty() && attached_filedata_.get()) { | 94 ++pending_op_count_; |
182 std::string* compressed_file_ptr = new std::string; | |
183 scoped_ptr<std::string> compressed_file(compressed_file_ptr); | |
184 #if defined(OS_WIN) | 95 #if defined(OS_WIN) |
185 base::FilePath attached_file(base::UTF8ToWide(attached_filename_)); | 96 base::FilePath attached_file(base::UTF8ToWide(attached_filename_)); |
186 #else | 97 #else |
187 base::FilePath attached_file(attached_filename_); | 98 base::FilePath attached_file(attached_filename_); |
188 #endif | 99 #endif |
189 BrowserThread::PostBlockingPoolTaskAndReply( | 100 BrowserThread::PostBlockingPoolTaskAndReply( |
190 FROM_HERE, | 101 FROM_HERE, |
191 base::Bind(&ZipFile, | 102 base::Bind(&FeedbackCommon::CompressFile, |
192 attached_file, | 103 this, |
193 *(attached_filedata_.get()), | 104 attached_file, |
194 compressed_file_ptr), | 105 std::string(), |
195 base::Bind(&FeedbackData::OnCompressFileComplete, | 106 base::Passed(&attached_filedata)), |
196 this, | 107 base::Bind(&FeedbackData::OnCompressComplete, this)); |
197 base::Passed(&compressed_file))); | |
198 } | |
199 } | 108 } |
200 | 109 |
201 void FeedbackData::OnGetTraceData( | 110 void FeedbackData::OnGetTraceData( |
202 int trace_id, | 111 int trace_id, |
203 scoped_refptr<base::RefCountedString> trace_data) { | 112 scoped_refptr<base::RefCountedString> trace_data) { |
204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 113 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
205 TracingManager* manager = TracingManager::Get(); | 114 TracingManager* manager = TracingManager::Get(); |
206 if (manager) | 115 if (manager) |
207 manager->DiscardTraceData(trace_id); | 116 manager->DiscardTraceData(trace_id); |
208 | 117 |
209 scoped_ptr<std::string> data(new std::string); | 118 scoped_ptr<std::string> data(new std::string); |
210 data->swap(trace_data->data()); | 119 data->swap(trace_data->data()); |
211 | 120 |
212 attached_filename_ = kTraceFilename; | 121 AddFile(kTraceFilename, data.Pass()); |
213 attached_filedata_ = data.Pass(); | |
214 attached_file_compression_complete_ = true; | |
215 trace_id_ = 0; | |
216 | 122 |
217 set_category_tag(kPerformanceCategoryTag); | 123 set_category_tag(kPerformanceCategoryTag); |
218 | 124 --pending_op_count_; |
| 125 trace_id_ = 0; |
219 SendReport(); | 126 SendReport(); |
220 } | 127 } |
221 | 128 |
222 void FeedbackData::OnCompressLogsComplete( | 129 void FeedbackData::OnCompressComplete() { |
223 scoped_ptr<std::string> compressed_logs) { | |
224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 130 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
225 | 131 --pending_op_count_; |
226 compressed_logs_ = compressed_logs.Pass(); | |
227 syslogs_compression_complete_ = true; | |
228 | |
229 SendReport(); | |
230 } | |
231 | |
232 void FeedbackData::OnCompressHistogramsComplete( | |
233 scoped_ptr<std::string> compressed_histograms) { | |
234 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
235 | |
236 compressed_histograms_ = compressed_histograms.Pass(); | |
237 histograms_compression_complete_ = true; | |
238 | |
239 SendReport(); | |
240 } | |
241 | |
242 void FeedbackData::OnCompressFileComplete( | |
243 scoped_ptr<std::string> compressed_file) { | |
244 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
245 | |
246 if (compressed_file.get()) { | |
247 attached_filedata_ = compressed_file.Pass(); | |
248 attached_filename_.append(kZipExt); | |
249 attached_file_compression_complete_ = true; | |
250 } else { | |
251 attached_filename_.clear(); | |
252 attached_filedata_.reset(NULL); | |
253 } | |
254 | |
255 SendReport(); | 132 SendReport(); |
256 } | 133 } |
257 | 134 |
258 bool FeedbackData::IsDataComplete() { | 135 bool FeedbackData::IsDataComplete() { |
259 return (!sys_info_.get() || syslogs_compression_complete_) && | 136 return pending_op_count_ == 0; |
260 (!histograms_.get() || histograms_compression_complete_) && | |
261 (!attached_filedata_.get() || attached_file_compression_complete_) && | |
262 !trace_id_ && | |
263 feedback_page_data_complete_; | |
264 } | 137 } |
265 | 138 |
266 void FeedbackData::SendReport() { | 139 void FeedbackData::SendReport() { |
267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 140 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
268 if (IsDataComplete() && !report_sent_) { | 141 if (IsDataComplete() && !report_sent_) { |
269 report_sent_ = true; | 142 report_sent_ = true; |
270 feedback_util::SendReport(this); | 143 send_report_.Run(this); |
271 } | 144 } |
272 } | 145 } |
273 | 146 |
274 } // namespace feedback | 147 } // namespace feedback |
OLD | NEW |