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.
42static 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
96int32 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
126LogWriter::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
135LogWriter::~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.
149void
150LogWriter::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
159void
160LogWriter::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
167void
168LogWriter::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
175void
176LogWriter::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
226void
227LogWriter::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