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 14 using blink::WebString; 15 16 namespace content { 17 18 namespace { 19 20 const int kResultHistogramSize = 50; 21 const int kCallsiteHistogramSize = 10; 22 23 int 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 65 WebDatabaseObserverImpl::WebDatabaseObserverImpl(IPC::SyncMessageFilter* sender) 66 : sender_(sender), 67 open_connections_(new storage::DatabaseConnectionsWrapper) { 68 DCHECK(sender); 69 } 70 71 WebDatabaseObserverImpl::~WebDatabaseObserverImpl() { 72 } 73 74 void 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 86 void 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 93 void 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 102 void 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 112 void 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 122 void 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 132 void 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 142 void 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 152 void 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 168 void WebDatabaseObserverImpl::WaitForAllDatabasesToClose() { 169 open_connections_->WaitForAllDatabasesToClose(); 170 } 171 172 void 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