blob: 35385c774f25c2dde04f90304a5620c3bdcfc514 [file] [log] [blame]
tedvessenes@gmail.com30dbbaa2012-01-13 09:11:01 +09001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
agl@chromium.org1c6dcf22009-07-23 08:57:21 +09002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ipc/ipc_logging.h"
6
agl@chromium.org1c6dcf22009-07-23 08:57:21 +09007#ifdef IPC_MESSAGE_LOG_ENABLED
agl@chromium.org1c6dcf22009-07-23 08:57:21 +09008#define IPC_MESSAGE_MACROS_LOG_ENABLED
9#endif
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090010
jhawkins@chromium.org9827bd12011-11-13 06:16:41 +090011#include "base/bind.h"
12#include "base/bind_helpers.h"
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090013#include "base/command_line.h"
ajwong@chromium.org8e2e3002011-09-22 03:05:41 +090014#include "base/location.h"
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090015#include "base/logging.h"
16#include "base/message_loop.h"
jrg@chromium.orgf2b21be2009-09-29 06:08:04 +090017#include "base/process_util.h"
avi@chromium.orge7eaf392013-06-11 15:32:18 +090018#include "base/strings/string_number_conversions.h"
19#include "base/strings/string_util.h"
brettw@chromium.org5b5f5e02011-01-01 10:01:06 +090020#include "base/threading/thread.h"
avi@chromium.org78a7e7b2013-06-29 00:20:02 +090021#include "base/time/time.h"
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090022#include "ipc/ipc_message_utils.h"
viettrungluu@chromium.org5254ca02012-12-15 07:56:28 +090023#include "ipc/ipc_sender.h"
avi@chromium.orge7eaf392013-06-11 15:32:18 +090024#include "ipc/ipc_switches.h"
25#include "ipc/ipc_sync_message.h"
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090026
27#if defined(OS_POSIX)
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090028#include <unistd.h>
29#endif
30
31#ifdef IPC_MESSAGE_LOG_ENABLED
32
33using base::Time;
34
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090035namespace IPC {
36
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090037const int kLogSendDelayMs = 100;
38
39// We use a pointer to the function table to avoid any linker dependencies on
40// all the traits used as IPC message parameters.
jam@chromium.org86a8de12010-12-09 08:34:16 +090041LogFunctionMap* Logging::log_function_map_;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090042
43Logging::Logging()
jrg@chromium.orgf2b21be2009-09-29 06:08:04 +090044 : enabled_(false),
45 enabled_on_stderr_(false),
scottbyer@chromium.orgf0cf0b72011-10-21 04:34:43 +090046 enabled_color_(false),
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090047 queue_invoke_later_pending_(false),
48 sender_(NULL),
xhwang@chromium.org0b2c2a52013-05-01 05:55:03 +090049 main_thread_(base::MessageLoop::current()),
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090050 consumer_(NULL) {
thomasvl@google.com9a242072010-07-23 23:18:59 +090051#if defined(OS_WIN)
52 // getenv triggers an unsafe warning. Simply check how big of a buffer
53 // would be needed to fetch the value to see if the enviornment variable is
54 // set.
55 size_t requiredSize = 0;
56 getenv_s(&requiredSize, NULL, 0, "CHROME_IPC_LOGGING");
57 bool logging_env_var_set = (requiredSize != 0);
scottbyer@chromium.orgf0cf0b72011-10-21 04:34:43 +090058 if (requiredSize <= 6) {
59 char buffer[6];
60 getenv_s(&requiredSize, buffer, sizeof(buffer), "CHROME_IPC_LOGGING");
61 if (requiredSize && !strncmp("color", buffer, 6))
62 enabled_color_ = true;
63 }
thomasvl@google.com9a242072010-07-23 23:18:59 +090064#else // !defined(OS_WIN)
scottbyer@chromium.orgf0cf0b72011-10-21 04:34:43 +090065 const char* ipc_logging = getenv("CHROME_IPC_LOGGING");
66 bool logging_env_var_set = (ipc_logging != NULL);
67 if (ipc_logging && !strcmp(ipc_logging, "color"))
68 enabled_color_ = true;
thomasvl@google.com9a242072010-07-23 23:18:59 +090069#endif //defined(OS_WIN)
70 if (logging_env_var_set) {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090071 enabled_ = true;
jrg@chromium.orgf2b21be2009-09-29 06:08:04 +090072 enabled_on_stderr_ = true;
73 }
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090074}
75
76Logging::~Logging() {
77}
78
satish@chromium.orgaa870602010-12-13 17:18:55 +090079Logging* Logging::GetInstance() {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090080 return Singleton<Logging>::get();
81}
82
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090083void Logging::SetConsumer(Consumer* consumer) {
84 consumer_ = consumer;
85}
86
87void Logging::Enable() {
jrg@chromium.orgf2b21be2009-09-29 06:08:04 +090088 enabled_ = true;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090089}
90
91void Logging::Disable() {
jrg@chromium.orgf2b21be2009-09-29 06:08:04 +090092 enabled_ = false;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090093}
94
95void Logging::OnSendLogs() {
96 queue_invoke_later_pending_ = false;
97 if (!sender_)
98 return;
99
100 Message* msg = new Message(
101 MSG_ROUTING_CONTROL, IPC_LOGGING_ID, Message::PRIORITY_NORMAL);
102 WriteParam(msg, queued_logs_);
103 queued_logs_.clear();
104 sender_->Send(msg);
105}
106
brettw@chromium.orgf947ed02012-06-12 07:35:26 +0900107void Logging::SetIPCSender(IPC::Sender* sender) {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900108 sender_ = sender;
109}
110
111void Logging::OnReceivedLoggingMessage(const Message& message) {
112 std::vector<LogData> data;
jbates@chromium.org0fc87362012-03-08 05:42:56 +0900113 PickleIterator iter(message);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900114 if (!ReadParam(&message, &iter, &data))
115 return;
116
117 for (size_t i = 0; i < data.size(); ++i) {
118 Log(data[i]);
119 }
120}
121
122void Logging::OnSendMessage(Message* message, const std::string& channel_id) {
123 if (!Enabled())
124 return;
125
126 if (message->is_reply()) {
127 LogData* data = message->sync_log_data();
128 if (!data)
129 return;
130
131 // This is actually the delayed reply to a sync message. Create a string
132 // of the output parameters, add it to the LogData that was earlier stashed
133 // with the reply, and log the result.
scottbyer@chromium.orgf0cf0b72011-10-21 04:34:43 +0900134 GenerateLogData("", *message, data, true);
samuel.xu@intel.com434073f2012-07-03 11:24:32 +0900135 data->channel = channel_id;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900136 Log(*data);
137 delete data;
138 message->set_sync_log_data(NULL);
139 } else {
140 // If the time has already been set (i.e. by ChannelProxy), keep that time
141 // instead as it's more accurate.
142 if (!message->sent_time())
143 message->set_sent_time(Time::Now().ToInternalValue());
144 }
145}
146
147void Logging::OnPreDispatchMessage(const Message& message) {
148 message.set_received_time(Time::Now().ToInternalValue());
149}
150
151void Logging::OnPostDispatchMessage(const Message& message,
152 const std::string& channel_id) {
153 if (!Enabled() ||
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900154 !message.sent_time() ||
jrg@chromium.orgf2b21be2009-09-29 06:08:04 +0900155 !message.received_time() ||
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900156 message.dont_log())
157 return;
158
159 LogData data;
scottbyer@chromium.orgf0cf0b72011-10-21 04:34:43 +0900160 GenerateLogData(channel_id, message, &data, true);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900161
xhwang@chromium.org0b2c2a52013-05-01 05:55:03 +0900162 if (base::MessageLoop::current() == main_thread_) {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900163 Log(data);
164 } else {
jhawkins@chromium.org9827bd12011-11-13 06:16:41 +0900165 main_thread_->PostTask(
166 FROM_HERE, base::Bind(&Logging::Log, base::Unretained(this), data));
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900167 }
168}
169
erg@google.com8aca7272010-08-19 03:33:57 +0900170void Logging::GetMessageText(uint32 type, std::string* name,
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900171 const Message* message,
erg@google.com8aca7272010-08-19 03:33:57 +0900172 std::string* params) {
jam@chromium.org86a8de12010-12-09 08:34:16 +0900173 if (!log_function_map_)
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900174 return;
175
jam@chromium.org86a8de12010-12-09 08:34:16 +0900176 LogFunctionMap::iterator it = log_function_map_->find(type);
177 if (it == log_function_map_->end()) {
178 if (name) {
179 *name = "[UNKNOWN MSG ";
180 *name += base::IntToString(type);
181 *name += " ]";
182 }
183 return;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900184 }
jam@chromium.org86a8de12010-12-09 08:34:16 +0900185
186 (*it->second)(name, message, params);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900187}
188
scottbyer@chromium.orgf0cf0b72011-10-21 04:34:43 +0900189const char* Logging::ANSIEscape(ANSIColor color) {
190 if (!enabled_color_)
191 return "";
192 switch (color) {
193 case ANSI_COLOR_RESET:
194 return "\033[m";
195 case ANSI_COLOR_BLACK:
196 return "\033[0;30m";
197 case ANSI_COLOR_RED:
198 return "\033[0;31m";
199 case ANSI_COLOR_GREEN:
200 return "\033[0;32m";
201 case ANSI_COLOR_YELLOW:
202 return "\033[0;33m";
203 case ANSI_COLOR_BLUE:
204 return "\033[0;34m";
205 case ANSI_COLOR_MAGENTA:
206 return "\033[0;35m";
207 case ANSI_COLOR_CYAN:
208 return "\033[0;36m";
209 case ANSI_COLOR_WHITE:
210 return "\033[0;37m";
211 }
212 return "";
213}
214
215Logging::ANSIColor Logging::DelayColor(double delay) {
216 if (delay < 0.1)
217 return ANSI_COLOR_GREEN;
218 if (delay < 0.25)
219 return ANSI_COLOR_BLACK;
220 if (delay < 0.5)
221 return ANSI_COLOR_YELLOW;
222 return ANSI_COLOR_RED;
223}
224
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900225void Logging::Log(const LogData& data) {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900226 if (consumer_) {
227 // We're in the browser process.
228 consumer_->Log(data);
229 } else {
230 // We're in the renderer or plugin processes.
231 if (sender_) {
232 queued_logs_.push_back(data);
233 if (!queue_invoke_later_pending_) {
234 queue_invoke_later_pending_ = true;
xhwang@chromium.org0b2c2a52013-05-01 05:55:03 +0900235 base::MessageLoop::current()->PostDelayedTask(
236 FROM_HERE,
237 base::Bind(&Logging::OnSendLogs, base::Unretained(this)),
tedvessenes@gmail.com30dbbaa2012-01-13 09:11:01 +0900238 base::TimeDelta::FromMilliseconds(kLogSendDelayMs));
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900239 }
240 }
241 }
jrg@chromium.orgf2b21be2009-09-29 06:08:04 +0900242 if (enabled_on_stderr_) {
243 std::string message_name;
244 if (data.message_name.empty()) {
groby@chromium.org6b4dc5e2013-03-19 07:33:04 +0900245 message_name = base::StringPrintf("[unknown type %d]", data.type);
jrg@chromium.orgf2b21be2009-09-29 06:08:04 +0900246 } else {
erg@google.com8aca7272010-08-19 03:33:57 +0900247 message_name = data.message_name;
jrg@chromium.orgf2b21be2009-09-29 06:08:04 +0900248 }
scottbyer@chromium.orgf0cf0b72011-10-21 04:34:43 +0900249 double receive_delay =
250 (Time::FromInternalValue(data.receive) -
251 Time::FromInternalValue(data.sent)).InSecondsF();
252 double dispatch_delay =
253 (Time::FromInternalValue(data.dispatch) -
254 Time::FromInternalValue(data.sent)).InSecondsF();
255 fprintf(stderr,
256 "ipc %s %d %s %s%s %s%s\n %18.5f %s%18.5f %s%18.5f%s\n",
jrg@chromium.orgf2b21be2009-09-29 06:08:04 +0900257 data.channel.c_str(),
258 data.routing_id,
erg@google.com8aca7272010-08-19 03:33:57 +0900259 data.flags.c_str(),
scottbyer@chromium.orgf0cf0b72011-10-21 04:34:43 +0900260 ANSIEscape(sender_ ? ANSI_COLOR_BLUE : ANSI_COLOR_CYAN),
jrg@chromium.orgf2b21be2009-09-29 06:08:04 +0900261 message_name.c_str(),
scottbyer@chromium.orgf0cf0b72011-10-21 04:34:43 +0900262 ANSIEscape(ANSI_COLOR_RESET),
263 data.params.c_str(),
264 Time::FromInternalValue(data.sent).ToDoubleT(),
265 ANSIEscape(DelayColor(receive_delay)),
266 Time::FromInternalValue(data.receive).ToDoubleT(),
267 ANSIEscape(DelayColor(dispatch_delay)),
268 Time::FromInternalValue(data.dispatch).ToDoubleT(),
269 ANSIEscape(ANSI_COLOR_RESET)
270 );
evan@chromium.org4bdd5df2009-07-29 02:04:37 +0900271 }
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900272}
273
274void GenerateLogData(const std::string& channel, const Message& message,
scottbyer@chromium.orgf0cf0b72011-10-21 04:34:43 +0900275 LogData* data, bool get_params) {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900276 if (message.is_reply()) {
277 // "data" should already be filled in.
erg@google.com8aca7272010-08-19 03:33:57 +0900278 std::string params;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900279 Logging::GetMessageText(data->type, NULL, &message, &params);
280
281 if (!data->params.empty() && !params.empty())
erg@google.com8aca7272010-08-19 03:33:57 +0900282 data->params += ", ";
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900283
erg@google.com8aca7272010-08-19 03:33:57 +0900284 data->flags += " DR";
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900285
286 data->params += params;
287 } else {
erg@google.com8aca7272010-08-19 03:33:57 +0900288 std::string flags;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900289 if (message.is_sync())
erg@google.com8aca7272010-08-19 03:33:57 +0900290 flags = "S";
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900291
292 if (message.is_reply())
erg@google.com8aca7272010-08-19 03:33:57 +0900293 flags += "R";
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900294
295 if (message.is_reply_error())
erg@google.com8aca7272010-08-19 03:33:57 +0900296 flags += "E";
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900297
erg@google.com8aca7272010-08-19 03:33:57 +0900298 std::string params, message_name;
scottbyer@chromium.orgf0cf0b72011-10-21 04:34:43 +0900299 Logging::GetMessageText(message.type(), &message_name, &message,
300 get_params ? &params : NULL);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900301
302 data->channel = channel;
303 data->routing_id = message.routing_id();
304 data->type = message.type();
305 data->flags = flags;
306 data->sent = message.sent_time();
307 data->receive = message.received_time();
308 data->dispatch = Time::Now().ToInternalValue();
309 data->params = params;
310 data->message_name = message_name;
311 }
312}
313
314}
315
316#endif // IPC_MESSAGE_LOG_ENABLED