1// Copyright 2013 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 "content/child/web_database_observer_impl.h" 6 7#include "base/metrics/histogram.h" 8#include "base/strings/string16.h" 9#include "content/common/database_messages.h" 10#include "third_party/WebKit/public/platform/WebCString.h" 11#include "third_party/WebKit/public/platform/WebString.h" 12#include "third_party/sqlite/sqlite3.h" 13 14using blink::WebString; 15 16namespace content { 17 18namespace { 19 20const int kResultHistogramSize = 50; 21const int kCallsiteHistogramSize = 10; 22 23int DetermineHistogramResult(int websql_error, int sqlite_error) { 24 // If we have a sqlite error, log it after trimming the extended bits. 25 // There are 26 possible values, but we leave room for some new ones. 26 if (sqlite_error) 27 return std::min(sqlite_error & 0xff, 30); 28 29 // Otherwise, websql_error may be an SQLExceptionCode, SQLErrorCode 30 // or a DOMExceptionCode, or -1 for success. 31 if (websql_error == -1) 32 return 0; // no error 33 34 // SQLExceptionCode starts at 1000 35 if (websql_error >= 1000) 36 websql_error -= 1000; 37 38 return std::min(websql_error + 30, kResultHistogramSize - 1); 39} 40 41#define UMA_HISTOGRAM_WEBSQL_RESULT(name, is_sync_database, \ 42 callsite, websql_error, sqlite_error) \ 43 do { \ 44 DCHECK(callsite < kCallsiteHistogramSize); \ 45 int result = DetermineHistogramResult(websql_error, sqlite_error); \ 46 if (is_sync_database) { \ 47 UMA_HISTOGRAM_ENUMERATION("websql.Sync." name, \ 48 result, kResultHistogramSize); \ 49 if (result) { \ 50 UMA_HISTOGRAM_ENUMERATION("websql.Sync." name ".ErrorSite", \ 51 callsite, kCallsiteHistogramSize); \ 52 } \ 53 } else { \ 54 UMA_HISTOGRAM_ENUMERATION("websql.Async." name, \ 55 result, kResultHistogramSize); \ 56 if (result) { \ 57 UMA_HISTOGRAM_ENUMERATION("websql.Async." name ".ErrorSite", \ 58 callsite, kCallsiteHistogramSize); \ 59 } \ 60 } \ 61 } while (0) 62 63} // namespace 64 65WebDatabaseObserverImpl::WebDatabaseObserverImpl(IPC::SyncMessageFilter* sender) 66 : sender_(sender), 67 open_connections_(new storage::DatabaseConnectionsWrapper) { 68 DCHECK(sender); 69} 70 71WebDatabaseObserverImpl::~WebDatabaseObserverImpl() { 72} 73 74void WebDatabaseObserverImpl::databaseOpened( 75 const WebString& origin_identifier, 76 const WebString& database_name, 77 const WebString& database_display_name, 78 unsigned long estimated_size) { 79 open_connections_->AddOpenConnection(origin_identifier.utf8(), 80 database_name); 81 sender_->Send(new DatabaseHostMsg_Opened( 82 origin_identifier.utf8(), database_name, 83 database_display_name, estimated_size)); 84} 85 86void WebDatabaseObserverImpl::databaseModified( 87 const WebString& origin_identifier, 88 const WebString& database_name) { 89 sender_->Send(new DatabaseHostMsg_Modified( 90 origin_identifier.utf8(), database_name)); 91} 92 93void WebDatabaseObserverImpl::databaseClosed( 94 const WebString& origin_identifier, 95 const WebString& database_name) { 96 sender_->Send(new DatabaseHostMsg_Closed( 97 origin_identifier.utf8(), database_name)); 98 open_connections_->RemoveOpenConnection(origin_identifier.utf8(), 99 database_name); 100} 101 102void WebDatabaseObserverImpl::reportOpenDatabaseResult( 103 const WebString& origin_identifier, 104 const WebString& database_name, 105 bool is_sync_database, 106 int callsite, int websql_error, int sqlite_error) { 107 UMA_HISTOGRAM_WEBSQL_RESULT("OpenResult", is_sync_database, callsite, 108 websql_error, sqlite_error); 109 HandleSqliteError(origin_identifier, database_name, sqlite_error); 110} 111 112void WebDatabaseObserverImpl::reportChangeVersionResult( 113 const WebString& origin_identifier, 114 const WebString& database_name, 115 bool is_sync_database, 116 int callsite, int websql_error, int sqlite_error) { 117 UMA_HISTOGRAM_WEBSQL_RESULT("ChangeVersionResult", is_sync_database, callsite, 118 websql_error, sqlite_error); 119 HandleSqliteError(origin_identifier, database_name, sqlite_error); 120} 121 122void WebDatabaseObserverImpl::reportStartTransactionResult( 123 const WebString& origin_identifier, 124 const WebString& database_name, 125 bool is_sync_database, 126 int callsite, int websql_error, int sqlite_error) { 127 UMA_HISTOGRAM_WEBSQL_RESULT("BeginResult", is_sync_database, callsite, 128 websql_error, sqlite_error); 129 HandleSqliteError(origin_identifier, database_name, sqlite_error); 130} 131 132void WebDatabaseObserverImpl::reportCommitTransactionResult( 133 const WebString& origin_identifier, 134 const WebString& database_name, 135 bool is_sync_database, 136 int callsite, int websql_error, int sqlite_error) { 137 UMA_HISTOGRAM_WEBSQL_RESULT("CommitResult", is_sync_database, callsite, 138 websql_error, sqlite_error); 139 HandleSqliteError(origin_identifier, database_name, sqlite_error); 140} 141 142void WebDatabaseObserverImpl::reportExecuteStatementResult( 143 const WebString& origin_identifier, 144 const WebString& database_name, 145 bool is_sync_database, 146 int callsite, int websql_error, int sqlite_error) { 147 UMA_HISTOGRAM_WEBSQL_RESULT("StatementResult", is_sync_database, callsite, 148 websql_error, sqlite_error); 149 HandleSqliteError(origin_identifier, database_name, sqlite_error); 150} 151 152void WebDatabaseObserverImpl::reportVacuumDatabaseResult( 153 const WebString& origin_identifier, 154 const WebString& database_name, 155 bool is_sync_database, 156 int sqlite_error) { 157 int result = DetermineHistogramResult(-1, sqlite_error); 158 if (is_sync_database) { 159 UMA_HISTOGRAM_ENUMERATION("websql.Sync.VacuumResult", 160 result, kResultHistogramSize); 161 } else { 162 UMA_HISTOGRAM_ENUMERATION("websql.Async.VacuumResult", 163 result, kResultHistogramSize); 164 } 165 HandleSqliteError(origin_identifier, database_name, sqlite_error); 166} 167 168void WebDatabaseObserverImpl::WaitForAllDatabasesToClose() { 169 open_connections_->WaitForAllDatabasesToClose(); 170} 171 172void WebDatabaseObserverImpl::HandleSqliteError( 173 const WebString& origin_identifier, 174 const WebString& database_name, 175 int error) { 176 // We filter out errors which the backend doesn't act on to avoid 177 // a unnecessary ipc traffic, this method can get called at a fairly 178 // high frequency (per-sqlstatement). 179 if (error == SQLITE_CORRUPT || error == SQLITE_NOTADB) { 180 sender_->Send(new DatabaseHostMsg_HandleSqliteError( 181 origin_identifier.utf8(), 182 database_name, 183 error)); 184 } 185} 186 187} // namespace content 188