1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "vm/log.h" |
6 | |
7 | #include "vm/flags.h" |
8 | #include "vm/isolate.h" |
9 | #include "vm/thread.h" |
10 | |
11 | namespace dart { |
12 | |
13 | DEFINE_FLAG(bool, force_log_flush, false, "Always flush log messages." ); |
14 | |
15 | // The following flag is useful when debugging on Android, since |
16 | // adb logcat truncates messages that are "too long" (and always |
17 | // flushing would result in too many short messages). |
18 | DEFINE_FLAG( |
19 | int, |
20 | force_log_flush_at_size, |
21 | 0, |
22 | "Flush log messages when buffer exceeds given size (disabled when 0)." ); |
23 | |
24 | DEFINE_FLAG(charp, |
25 | isolate_log_filter, |
26 | nullptr, |
27 | "Log isolates whose name include the filter. " |
28 | "Default: service isolate log messages are suppressed " |
29 | "(specify 'vm-service' to log them)." ); |
30 | |
31 | Log::Log(LogPrinter printer) |
32 | : printer_(printer), manual_flush_(0), buffer_(0) {} |
33 | |
34 | Log::~Log() { |
35 | // Did someone enable manual flushing and then forgot to Flush? |
36 | ASSERT(cursor() == 0); |
37 | } |
38 | |
39 | Log* Log::Current() { |
40 | Thread* thread = Thread::Current(); |
41 | if (thread == nullptr) { |
42 | OSThread* os_thread = OSThread::Current(); |
43 | ASSERT(os_thread != nullptr); |
44 | return os_thread->log(); |
45 | } |
46 | IsolateGroup* isolate_group = thread->isolate_group(); |
47 | if ((isolate_group != nullptr) && |
48 | Log::ShouldLogForIsolateGroup(isolate_group)) { |
49 | OSThread* os_thread = thread->os_thread(); |
50 | ASSERT(os_thread != nullptr); |
51 | return os_thread->log(); |
52 | } else { |
53 | return Log::NoOpLog(); |
54 | } |
55 | } |
56 | |
57 | void Log::Print(const char* format, ...) { |
58 | if (this == NoOpLog()) { |
59 | return; |
60 | } |
61 | |
62 | va_list args; |
63 | va_start(args, format); |
64 | VPrint(format, args); |
65 | va_end(args); |
66 | } |
67 | |
68 | void Log::VPrint(const char* format, va_list args) { |
69 | if (this == NoOpLog()) { |
70 | return; |
71 | } |
72 | |
73 | // Measure. |
74 | va_list measure_args; |
75 | va_copy(measure_args, args); |
76 | intptr_t len = Utils::VSNPrint(nullptr, 0, format, measure_args); |
77 | va_end(measure_args); |
78 | |
79 | // Print. |
80 | char* buffer = reinterpret_cast<char*>(malloc(len + 1)); |
81 | va_list print_args; |
82 | va_copy(print_args, args); |
83 | Utils::VSNPrint(buffer, (len + 1), format, print_args); |
84 | va_end(print_args); |
85 | |
86 | // Append. |
87 | // NOTE: does not append the '\0' character. |
88 | for (intptr_t i = 0; i < len; i++) { |
89 | buffer_.Add(buffer[i]); |
90 | } |
91 | free(buffer); |
92 | |
93 | if (ShouldFlush()) { |
94 | Flush(); |
95 | } |
96 | } |
97 | |
98 | void Log::Flush(const intptr_t cursor) { |
99 | if (this == NoOpLog()) { |
100 | return; |
101 | } |
102 | if (buffer_.is_empty()) { |
103 | return; |
104 | } |
105 | if (buffer_.length() <= cursor) { |
106 | return; |
107 | } |
108 | TerminateString(); |
109 | const char* str = &buffer_[cursor]; |
110 | ASSERT(str != nullptr); |
111 | printer_("%s" , str); |
112 | buffer_.TruncateTo(cursor); |
113 | } |
114 | |
115 | void Log::Clear() { |
116 | if (this == NoOpLog()) { |
117 | return; |
118 | } |
119 | buffer_.TruncateTo(0); |
120 | } |
121 | |
122 | intptr_t Log::cursor() const { |
123 | return buffer_.length(); |
124 | } |
125 | |
126 | bool Log::ShouldLogForIsolateGroup(const IsolateGroup* isolate_group) { |
127 | if (FLAG_isolate_log_filter == nullptr) { |
128 | if (IsolateGroup::IsVMInternalIsolateGroup(isolate_group)) { |
129 | // By default, do not log for the service or kernel isolates. |
130 | return false; |
131 | } |
132 | return true; |
133 | } |
134 | const char* name = isolate_group->source()->name; |
135 | ASSERT(name != nullptr); |
136 | if (strstr(name, FLAG_isolate_log_filter) == nullptr) { |
137 | // Filter does not match, do not log for this isolate. |
138 | return false; |
139 | } |
140 | return true; |
141 | } |
142 | |
143 | Log Log::noop_log_; |
144 | Log* Log::NoOpLog() { |
145 | return &noop_log_; |
146 | } |
147 | |
148 | void Log::TerminateString() { |
149 | if (this == NoOpLog()) { |
150 | return; |
151 | } |
152 | buffer_.Add('\0'); |
153 | } |
154 | |
155 | void Log::EnableManualFlush() { |
156 | if (this == NoOpLog()) { |
157 | return; |
158 | } |
159 | manual_flush_++; |
160 | } |
161 | |
162 | void Log::DisableManualFlush(const intptr_t cursor) { |
163 | if (this == NoOpLog()) { |
164 | return; |
165 | } |
166 | |
167 | manual_flush_--; |
168 | ASSERT(manual_flush_ >= 0); |
169 | if (manual_flush_ == 0) { |
170 | Flush(cursor); |
171 | } |
172 | } |
173 | |
174 | bool Log::ShouldFlush() const { |
175 | return ((manual_flush_ == 0) || FLAG_force_log_flush || |
176 | ((FLAG_force_log_flush_at_size > 0) && |
177 | (cursor() > FLAG_force_log_flush_at_size))); |
178 | } |
179 | |
180 | void LogBlock::Initialize() { |
181 | log_->EnableManualFlush(); |
182 | } |
183 | |
184 | LogBlock::~LogBlock() { |
185 | log_->DisableManualFlush(cursor_); |
186 | } |
187 | |
188 | } // namespace dart |
189 | |