xref: /haiku/src/apps/cortex/addons/LoggingConsumer/LogWriter.cpp (revision 1deede7388b04dbeec5af85cae7164735ea9e70d)
1 /*
2  * Copyright 1991-1999, Be Incorporated.
3  * Copyright (c) 1999-2000, Eric Moon.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions, and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions, and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
28  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 
33 // LogWriter.cpp
34 
35 #include "LogWriter.h"
36 #include <stdio.h>
37 #include <string.h>
38 
39 // this is the simpleminded implementation of a lookup function;
40 // could also use some sort of map.  this has the advantage of not
41 // requiring any runtime initialization.
42 static const char* log_what_to_string(log_what code)
43 {
44 	const char* s = "Unknown log_what code!";
45 	switch (code)
46 	{
47 	case LOG_QUIT : s = "LOG_QUIT"; break;
48 	case LOG_SET_RUN_MODE  : s = "LOG_SET_RUN_MODE"; break;
49 	case LOG_PREROLL : s = "LOG_PREROLL"; break;
50 	case LOG_SET_TIME_SOURCE : s = "LOG_SET_TIME_SOURCE"; break;
51 	case LOG_REQUEST_COMPLETED : s = "LOG_REQUEST_COMPLETED"; break;
52 	case LOG_GET_PARAM_VALUE : s = "LOG_GET_PARAM_VALUE"; break;
53 	case LOG_SET_PARAM_VALUE : s = "LOG_SET_PARAM_VALUE"; break;
54 	case LOG_FORMAT_SUGG_REQ : s = "LOG_FORMAT_SUGG_REQ"; break;
55 	case LOG_FORMAT_PROPOSAL : s = "LOG_FORMAT_PROPOSAL"; break;
56 	case LOG_FORMAT_CHANGE_REQ : s = "LOG_FORMAT_CHANGE_REQ"; break;
57 	case LOG_SET_BUFFER_GROUP : s = "LOG_SET_BUFFER_GROUP"; break;
58 	case LOG_VIDEO_CLIP_CHANGED : s = "LOG_VIDEO_CLIP_CHANGED"; break;
59 	case LOG_GET_LATENCY : s = "LOG_GET_LATENCY"; break;
60 	case LOG_PREPARE_TO_CONNECT : s = "LOG_PREPARE_TO_CONNECT"; break;
61 	case LOG_CONNECT : s = "LOG_CONNECT"; break;
62 	case LOG_DISCONNECT : s = "LOG_DISCONNECT"; break;
63 	case LOG_LATE_NOTICE_RECEIVED : s = "LOG_LATE_NOTICE_RECEIVED"; break;
64 	case LOG_ENABLE_OUTPUT : s = "LOG_ENABLE_OUTPUT"; break;
65 	case LOG_SET_PLAY_RATE : s = "LOG_SET_PLAY_RATE"; break;
66 	case LOG_ADDITIONAL_BUFFER : s = "LOG_ADDITIONAL_BUFFER"; break;
67 	case LOG_LATENCY_CHANGED : s = "LOG_LATENCY_CHANGED"; break;
68 	case LOG_HANDLE_MESSAGE : s = "LOG_HANDLE_MESSAGE"; break;
69 	case LOG_ACCEPT_FORMAT : s = "LOG_ACCEPT_FORMAT"; break;
70 	case LOG_BUFFER_RECEIVED : s = "LOG_BUFFER_RECEIVED"; break;
71 	case LOG_PRODUCER_DATA_STATUS : s = "LOG_PRODUCER_DATA_STATUS"; break;
72 	case LOG_CONNECTED : s = "LOG_CONNECTED"; break;
73 	case LOG_DISCONNECTED : s = "LOG_DISCONNECTED"; break;
74 	case LOG_FORMAT_CHANGED : s = "LOG_FORMAT_CHANGED"; break;
75 	case LOG_SEEK_TAG : s = "LOG_SEEK_TAG"; break;
76 	case LOG_REGISTERED : s = "LOG_REGISTERED"; break;
77 	case LOG_START : s = "LOG_START"; break;
78 	case LOG_STOP : s = "LOG_STOP"; break;
79 	case LOG_SEEK : s = "LOG_SEEK"; break;
80 	case LOG_TIMEWARP : s = "LOG_TIMEWARP"; break;
81 	case LOG_HANDLE_EVENT : s = "LOG_HANDLE_EVENT"; break;
82 	case LOG_HANDLE_UNKNOWN : s = "LOG_HANDLE_UNKNOWN"; break;
83 	case LOG_BUFFER_HANDLED : s = "LOG_BUFFER_HANDLED"; break;
84 	case LOG_START_HANDLED : s = "LOG_START_HANDLED"; break;
85 	case LOG_STOP_HANDLED : s = "LOG_STOP_HANDLED"; break;
86 	case LOG_SEEK_HANDLED : s = "LOG_SEEK_HANDLED"; break;
87 	case LOG_WARP_HANDLED : s = "LOG_WARP_HANDLED"; break;
88 	case LOG_DATA_STATUS_HANDLED : s = "LOG_DATA_STATUS_HANDLED"; break;
89 	case LOG_SET_PARAM_HANDLED : s = "LOG_SET_PARAM_HANDLED"; break;
90 	case LOG_INVALID_PARAM_HANDLED : s = "LOG_INVALID_PARAM_HANDLED"; break;
91 	}
92 	return s;
93 }
94 
95 // Logging thread function
96 int32 LogWriterLoggingThread(void* arg)
97 {
98 	LogWriter* obj = static_cast<LogWriter*>(arg);
99 	port_id port = obj->mPort;
100 
101 	// event loop
102 
103 	bool done = false;
104 	do
105 	{
106 		log_message msg;
107 		int32 what;
108 		status_t n_bytes = ::read_port(port, &what, &msg, sizeof(log_message));
109 		if (n_bytes > 0)
110 		{
111 			obj->HandleMessage((log_what) what, msg);
112 			if (LOG_QUIT == what) done = true;
113 		}
114 		else
115 		{
116 			fprintf(stderr, "LogWriter failed (%s) in ::read_port()!\n", strerror(n_bytes));
117 		}
118 	} while (!done);
119 
120 	// got the "quit" message; now we're done
121 	return 0;
122 }
123 
124 // --------------------
125 // LogWriter class implementation
126 LogWriter::LogWriter(const entry_ref& logRef)
127 	:	mLogRef(logRef)
128 {
129 	mPort = ::create_port(64, "LogWriter");
130 	mLogThread = ::spawn_thread(&LogWriterLoggingThread, "LogWriter", B_NORMAL_PRIORITY, this);
131 	mLogFile.SetTo(&logRef, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
132 	::resume_thread(mLogThread);
133 }
134 
135 LogWriter::~LogWriter()
136 {
137 	printf("LogWriter::~LogWriter() called\n");
138 	status_t err;
139 	log_message msg;
140 
141 	Log(LOG_QUIT, msg);
142 	::wait_for_thread(mLogThread, &err);
143 }
144 
145 // Log a message
146 //
147 // This method, called by the client, really just enqueues a message to the writer thread,
148 // which will deal with it in the HandleMessage() method.
149 void
150 LogWriter::Log(log_what what, const log_message& data)
151 {
152 	bigtime_t now = ::system_time();
153 	log_message& nc_data = const_cast<log_message&>(data);
154 	nc_data.tstamp = now;
155 	::write_port(mPort, (int32) what, &data, sizeof(log_message));
156 }
157 
158 // Enable or disable a particular log_what code's output
159 void
160 LogWriter::SetEnabled(log_what what, bool enable)
161 {
162 	if (enable)	 mFilters.erase(what);
163 	else mFilters.insert(what);
164 }
165 
166 // enabling everything means just clearing out the filter set
167 void
168 LogWriter::EnableAllMessages()
169 {
170 	mFilters.clear();
171 }
172 
173 // disabling everything is more tedious -- we have to add them all to the
174 // filter set, one by one
175 void
176 LogWriter::DisableAllMessages()
177 {
178 //	mFilters.insert(LOG_QUIT);				// don't disable our quit messages
179 	mFilters.insert(LOG_SET_RUN_MODE);
180 	mFilters.insert(LOG_PREROLL);
181 	mFilters.insert(LOG_SET_TIME_SOURCE);
182 	mFilters.insert(LOG_REQUEST_COMPLETED);
183 	mFilters.insert(LOG_GET_PARAM_VALUE);
184 	mFilters.insert(LOG_SET_PARAM_VALUE);
185 	mFilters.insert(LOG_FORMAT_SUGG_REQ);
186 	mFilters.insert(LOG_FORMAT_PROPOSAL);
187 	mFilters.insert(LOG_FORMAT_CHANGE_REQ);
188 	mFilters.insert(LOG_SET_BUFFER_GROUP);
189 	mFilters.insert(LOG_VIDEO_CLIP_CHANGED);
190 	mFilters.insert(LOG_GET_LATENCY);
191 	mFilters.insert(LOG_PREPARE_TO_CONNECT);
192 	mFilters.insert(LOG_CONNECT);
193 	mFilters.insert(LOG_DISCONNECT);
194 	mFilters.insert(LOG_LATE_NOTICE_RECEIVED);
195 	mFilters.insert(LOG_ENABLE_OUTPUT);
196 	mFilters.insert(LOG_SET_PLAY_RATE);
197 	mFilters.insert(LOG_ADDITIONAL_BUFFER);
198 	mFilters.insert(LOG_LATENCY_CHANGED);
199 	mFilters.insert(LOG_HANDLE_MESSAGE);
200 	mFilters.insert(LOG_ACCEPT_FORMAT);
201 	mFilters.insert(LOG_BUFFER_RECEIVED);
202 	mFilters.insert(LOG_PRODUCER_DATA_STATUS);
203 	mFilters.insert(LOG_CONNECTED);
204 	mFilters.insert(LOG_DISCONNECTED);
205 	mFilters.insert(LOG_FORMAT_CHANGED);
206 	mFilters.insert(LOG_SEEK_TAG);
207 	mFilters.insert(LOG_REGISTERED);
208 	mFilters.insert(LOG_START);
209 	mFilters.insert(LOG_STOP);
210 	mFilters.insert(LOG_SEEK);
211 	mFilters.insert(LOG_TIMEWARP);
212 	mFilters.insert(LOG_HANDLE_EVENT);
213 //	mFilters.insert(LOG_HANDLE_UNKNOWN);			// don't disable the "unknown message" messages
214 	mFilters.insert(LOG_BUFFER_HANDLED);
215 	mFilters.insert(LOG_START_HANDLED);
216 	mFilters.insert(LOG_STOP_HANDLED);
217 	mFilters.insert(LOG_SEEK_HANDLED);
218 	mFilters.insert(LOG_WARP_HANDLED);
219 	mFilters.insert(LOG_DATA_STATUS_HANDLED);
220 	mFilters.insert(LOG_SET_PARAM_HANDLED);
221 	mFilters.insert(LOG_INVALID_PARAM_HANDLED);
222 }
223 
224 // Writer thread's message handling function -- this is where messages are actuall
225 // formatted and written to the log file
226 void
227 LogWriter::HandleMessage(log_what what, const log_message& msg)
228 {
229 	char buf[256];		// scratch buffer for building logged output
230 
231 	// if we've been told to ignore this message type, just return without doing anything else
232 	if (mFilters.find(what) != mFilters.end()) return;
233 
234 	// always write the message's type and timestamp
235 	sprintf(buf, "%-24s : realtime = %" B_PRIdBIGTIME ", perftime = %" B_PRIdBIGTIME "\n",
236 		log_what_to_string(what), msg.tstamp, msg.now);
237 	mWriteBuf = buf;
238 
239 	// put any special per-message-type handling here
240 	switch (what)
241 	{
242 	case LOG_QUIT:
243 		mWriteBuf += "\tLogWriter thread terminating\n";
244 		break;
245 
246 	case LOG_BUFFER_RECEIVED:
247 		if (msg.buffer_data.offset < 0)
248 		{
249 			sprintf(buf, "\tstart = %" B_PRIdBIGTIME ", offset = %" B_PRIdBIGTIME "\n",
250 				msg.buffer_data.start_time, msg.buffer_data.offset);
251 			mWriteBuf += buf;
252 			sprintf(buf, "\tBuffer received *LATE*\n");
253 			mWriteBuf += buf;
254 		}
255 		break;
256 
257 	case LOG_SET_PARAM_HANDLED:
258 		sprintf(buf, "\tparam id = %" B_PRId32 ", value = %f\n", msg.param.id, msg.param.value);
259 		mWriteBuf += buf;
260 		break;
261 
262 	case LOG_INVALID_PARAM_HANDLED:
263 	case LOG_GET_PARAM_VALUE:
264 		sprintf(buf, "\tparam id = %" B_PRId32 "\n", msg.param.id);
265 		mWriteBuf += buf;
266 		break;
267 
268 	case LOG_BUFFER_HANDLED:
269 		sprintf(buf, "\tstart = %" B_PRIdBIGTIME ", offset = %" B_PRIdBIGTIME "\n",
270 			msg.buffer_data.start_time, msg.buffer_data.offset);
271 		mWriteBuf += buf;
272 		if (msg.buffer_data.offset < 0)
273 		{
274 			sprintf(buf, "\tBuffer handled *LATE*\n");
275 			mWriteBuf += buf;
276 		}
277 		break;
278 
279 	case LOG_DATA_STATUS_HANDLED:
280 		sprintf(buf, "\tstatus = %d\n", int(msg.data_status.status));
281 		mWriteBuf += buf;
282 		break;
283 
284 	case LOG_HANDLE_UNKNOWN:
285 		sprintf(buf, "\tUNKNOWN EVENT CODE: %d\n", int(msg.unknown.what));
286 		mWriteBuf += buf;
287 		break;
288 
289 	default:
290 		break;
291 	}
292 
293 	// actually write the log message to the file now
294 	mLogFile.Write(mWriteBuf.String(), mWriteBuf.Length());
295 }
296