xref: /haiku/src/apps/cortex/addons/LoggingConsumer/LogWriter.cpp (revision 16d5c24e533eb14b7b8a99ee9f3ec9ba66335b1e)
1 // LogWriter.cpp
2 
3 #include "LogWriter.h"
4 #include <stdio.h>
5 #include <string.h>
6 
7 // this is the simpleminded implementation of a lookup function;
8 // could also use some sort of map.  this has the advantage of not
9 // requiring any runtime initialization.
10 static const char* log_what_to_string(log_what code)
11 {
12 	const char* s = "Unknown log_what code!";
13 	switch (code)
14 	{
15 	case LOG_QUIT : s = "LOG_QUIT"; break;
16 	case LOG_SET_RUN_MODE  : s = "LOG_SET_RUN_MODE"; break;
17 	case LOG_PREROLL : s = "LOG_PREROLL"; break;
18 	case LOG_SET_TIME_SOURCE : s = "LOG_SET_TIME_SOURCE"; break;
19 	case LOG_REQUEST_COMPLETED : s = "LOG_REQUEST_COMPLETED"; break;
20 	case LOG_GET_PARAM_VALUE : s = "LOG_GET_PARAM_VALUE"; break;
21 	case LOG_SET_PARAM_VALUE : s = "LOG_SET_PARAM_VALUE"; break;
22 	case LOG_FORMAT_SUGG_REQ : s = "LOG_FORMAT_SUGG_REQ"; break;
23 	case LOG_FORMAT_PROPOSAL : s = "LOG_FORMAT_PROPOSAL"; break;
24 	case LOG_FORMAT_CHANGE_REQ : s = "LOG_FORMAT_CHANGE_REQ"; break;
25 	case LOG_SET_BUFFER_GROUP : s = "LOG_SET_BUFFER_GROUP"; break;
26 	case LOG_VIDEO_CLIP_CHANGED : s = "LOG_VIDEO_CLIP_CHANGED"; break;
27 	case LOG_GET_LATENCY : s = "LOG_GET_LATENCY"; break;
28 	case LOG_PREPARE_TO_CONNECT : s = "LOG_PREPARE_TO_CONNECT"; break;
29 	case LOG_CONNECT : s = "LOG_CONNECT"; break;
30 	case LOG_DISCONNECT : s = "LOG_DISCONNECT"; break;
31 	case LOG_LATE_NOTICE_RECEIVED : s = "LOG_LATE_NOTICE_RECEIVED"; break;
32 	case LOG_ENABLE_OUTPUT : s = "LOG_ENABLE_OUTPUT"; break;
33 	case LOG_SET_PLAY_RATE : s = "LOG_SET_PLAY_RATE"; break;
34 	case LOG_ADDITIONAL_BUFFER : s = "LOG_ADDITIONAL_BUFFER"; break;
35 	case LOG_LATENCY_CHANGED : s = "LOG_LATENCY_CHANGED"; break;
36 	case LOG_HANDLE_MESSAGE : s = "LOG_HANDLE_MESSAGE"; break;
37 	case LOG_ACCEPT_FORMAT : s = "LOG_ACCEPT_FORMAT"; break;
38 	case LOG_BUFFER_RECEIVED : s = "LOG_BUFFER_RECEIVED"; break;
39 	case LOG_PRODUCER_DATA_STATUS : s = "LOG_PRODUCER_DATA_STATUS"; break;
40 	case LOG_CONNECTED : s = "LOG_CONNECTED"; break;
41 	case LOG_DISCONNECTED : s = "LOG_DISCONNECTED"; break;
42 	case LOG_FORMAT_CHANGED : s = "LOG_FORMAT_CHANGED"; break;
43 	case LOG_SEEK_TAG : s = "LOG_SEEK_TAG"; break;
44 	case LOG_REGISTERED : s = "LOG_REGISTERED"; break;
45 	case LOG_START : s = "LOG_START"; break;
46 	case LOG_STOP : s = "LOG_STOP"; break;
47 	case LOG_SEEK : s = "LOG_SEEK"; break;
48 	case LOG_TIMEWARP : s = "LOG_TIMEWARP"; break;
49 	case LOG_HANDLE_EVENT : s = "LOG_HANDLE_EVENT"; break;
50 	case LOG_HANDLE_UNKNOWN : s = "LOG_HANDLE_UNKNOWN"; break;
51 	case LOG_BUFFER_HANDLED : s = "LOG_BUFFER_HANDLED"; break;
52 	case LOG_START_HANDLED : s = "LOG_START_HANDLED"; break;
53 	case LOG_STOP_HANDLED : s = "LOG_STOP_HANDLED"; break;
54 	case LOG_SEEK_HANDLED : s = "LOG_SEEK_HANDLED"; break;
55 	case LOG_WARP_HANDLED : s = "LOG_WARP_HANDLED"; break;
56 	case LOG_DATA_STATUS_HANDLED : s = "LOG_DATA_STATUS_HANDLED"; break;
57 	case LOG_SET_PARAM_HANDLED : s = "LOG_SET_PARAM_HANDLED"; break;
58 	case LOG_INVALID_PARAM_HANDLED : s = "LOG_INVALID_PARAM_HANDLED"; break;
59 	}
60 	return s;
61 }
62 
63 // Logging thread function
64 int32 LogWriterLoggingThread(void* arg)
65 {
66 	LogWriter* obj = static_cast<LogWriter*>(arg);
67 	port_id port = obj->mPort;
68 
69 	// event loop
70 
71 	bool done = false;
72 	do
73 	{
74 		log_message msg;
75 		int32 what;
76 		status_t n_bytes = ::read_port(port, &what, &msg, sizeof(log_message));
77 		if (n_bytes > 0)
78 		{
79 			obj->HandleMessage((log_what) what, msg);
80 			if (LOG_QUIT == what) done = true;
81 		}
82 		else
83 		{
84 			fprintf(stderr, "LogWriter failed (%s) in ::read_port()!\n", strerror(n_bytes));
85 		}
86 	} while (!done);
87 
88 	// got the "quit" message; now we're done
89 	return 0;
90 }
91 
92 // --------------------
93 // LogWriter class implementation
94 LogWriter::LogWriter(const entry_ref& logRef)
95 	:	mLogRef(logRef)
96 {
97 	mPort = ::create_port(64, "LogWriter");
98 	mLogThread = ::spawn_thread(&LogWriterLoggingThread, "LogWriter", B_NORMAL_PRIORITY, this);
99 	mLogFile.SetTo(&logRef, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
100 	::resume_thread(mLogThread);
101 }
102 
103 LogWriter::~LogWriter()
104 {
105 	printf("LogWriter::~LogWriter() called\n");
106 	status_t err;
107 	log_message msg;
108 
109 	Log(LOG_QUIT, msg);
110 	::wait_for_thread(mLogThread, &err);
111 }
112 
113 // Log a message
114 //
115 // This method, called by the client, really just enqueues a message to the writer thread,
116 // which will deal with it in the HandleMessage() method.
117 void
118 LogWriter::Log(log_what what, const log_message& data)
119 {
120 	bigtime_t now = ::system_time();
121 	log_message& nc_data = const_cast<log_message&>(data);
122 	nc_data.tstamp = now;
123 	::write_port(mPort, (int32) what, &data, sizeof(log_message));
124 }
125 
126 // Enable or disable a particular log_what code's output
127 void
128 LogWriter::SetEnabled(log_what what, bool enable)
129 {
130 	if (enable)	 mFilters.erase(what);
131 	else mFilters.insert(what);
132 }
133 
134 // enabling everything means just clearing out the filter set
135 void
136 LogWriter::EnableAllMessages()
137 {
138 	mFilters.clear();
139 }
140 
141 // disabling everything is more tedious -- we have to add them all to the
142 // filter set, one by one
143 void
144 LogWriter::DisableAllMessages()
145 {
146 //	mFilters.insert(LOG_QUIT);				// don't disable our quit messages
147 	mFilters.insert(LOG_SET_RUN_MODE);
148 	mFilters.insert(LOG_PREROLL);
149 	mFilters.insert(LOG_SET_TIME_SOURCE);
150 	mFilters.insert(LOG_REQUEST_COMPLETED);
151 	mFilters.insert(LOG_GET_PARAM_VALUE);
152 	mFilters.insert(LOG_SET_PARAM_VALUE);
153 	mFilters.insert(LOG_FORMAT_SUGG_REQ);
154 	mFilters.insert(LOG_FORMAT_PROPOSAL);
155 	mFilters.insert(LOG_FORMAT_CHANGE_REQ);
156 	mFilters.insert(LOG_SET_BUFFER_GROUP);
157 	mFilters.insert(LOG_VIDEO_CLIP_CHANGED);
158 	mFilters.insert(LOG_GET_LATENCY);
159 	mFilters.insert(LOG_PREPARE_TO_CONNECT);
160 	mFilters.insert(LOG_CONNECT);
161 	mFilters.insert(LOG_DISCONNECT);
162 	mFilters.insert(LOG_LATE_NOTICE_RECEIVED);
163 	mFilters.insert(LOG_ENABLE_OUTPUT);
164 	mFilters.insert(LOG_SET_PLAY_RATE);
165 	mFilters.insert(LOG_ADDITIONAL_BUFFER);
166 	mFilters.insert(LOG_LATENCY_CHANGED);
167 	mFilters.insert(LOG_HANDLE_MESSAGE);
168 	mFilters.insert(LOG_ACCEPT_FORMAT);
169 	mFilters.insert(LOG_BUFFER_RECEIVED);
170 	mFilters.insert(LOG_PRODUCER_DATA_STATUS);
171 	mFilters.insert(LOG_CONNECTED);
172 	mFilters.insert(LOG_DISCONNECTED);
173 	mFilters.insert(LOG_FORMAT_CHANGED);
174 	mFilters.insert(LOG_SEEK_TAG);
175 	mFilters.insert(LOG_REGISTERED);
176 	mFilters.insert(LOG_START);
177 	mFilters.insert(LOG_STOP);
178 	mFilters.insert(LOG_SEEK);
179 	mFilters.insert(LOG_TIMEWARP);
180 	mFilters.insert(LOG_HANDLE_EVENT);
181 //	mFilters.insert(LOG_HANDLE_UNKNOWN);			// don't disable the "unknown message" messages
182 	mFilters.insert(LOG_BUFFER_HANDLED);
183 	mFilters.insert(LOG_START_HANDLED);
184 	mFilters.insert(LOG_STOP_HANDLED);
185 	mFilters.insert(LOG_SEEK_HANDLED);
186 	mFilters.insert(LOG_WARP_HANDLED);
187 	mFilters.insert(LOG_DATA_STATUS_HANDLED);
188 	mFilters.insert(LOG_SET_PARAM_HANDLED);
189 	mFilters.insert(LOG_INVALID_PARAM_HANDLED);
190 }
191 
192 // Writer thread's message handling function -- this is where messages are actuall
193 // formatted and written to the log file
194 void
195 LogWriter::HandleMessage(log_what what, const log_message& msg)
196 {
197 	char buf[256];		// scratch buffer for building logged output
198 
199 	// if we've been told to ignore this message type, just return without doing anything else
200 	if (mFilters.find(what) != mFilters.end()) return;
201 
202 	// always write the message's type and timestamp
203 	sprintf(buf, "%-24s : realtime = %Ld, perftime = %Ld\n", log_what_to_string(what), msg.tstamp, msg.now);
204 	mWriteBuf = buf;
205 
206 	// put any special per-message-type handling here
207 	switch (what)
208 	{
209 	case LOG_QUIT:
210 		mWriteBuf += "\tLogWriter thread terminating\n";
211 		break;
212 
213 	case LOG_BUFFER_RECEIVED:
214 		if (msg.buffer_data.offset < 0)
215 		{
216 			sprintf(buf, "\tstart = %Ld, offset = %Ld\n", msg.buffer_data.start_time, msg.buffer_data.offset);
217 			mWriteBuf += buf;
218 			sprintf(buf, "\tBuffer received *LATE*\n");
219 			mWriteBuf += buf;
220 		}
221 		break;
222 
223 	case LOG_SET_PARAM_HANDLED:
224 		sprintf(buf, "\tparam id = %ld, value = %f\n", msg.param.id, msg.param.value);
225 		mWriteBuf += buf;
226 		break;
227 
228 	case LOG_INVALID_PARAM_HANDLED:
229 	case LOG_GET_PARAM_VALUE:
230 		sprintf(buf, "\tparam id = %ld\n", msg.param.id);
231 		mWriteBuf += buf;
232 		break;
233 
234 	case LOG_BUFFER_HANDLED:
235 		sprintf(buf, "\tstart = %Ld, offset = %Ld\n", msg.buffer_data.start_time, msg.buffer_data.offset);
236 		mWriteBuf += buf;
237 		if (msg.buffer_data.offset < 0)
238 		{
239 			sprintf(buf, "\tBuffer handled *LATE*\n");
240 			mWriteBuf += buf;
241 		}
242 		break;
243 
244 	case LOG_DATA_STATUS_HANDLED:
245 		sprintf(buf, "\tstatus = %d\n", int(msg.data_status.status));
246 		mWriteBuf += buf;
247 		break;
248 
249 	case LOG_HANDLE_UNKNOWN:
250 		sprintf(buf, "\tUNKNOWN EVENT CODE: %d\n", int(msg.unknown.what));
251 		mWriteBuf += buf;
252 		break;
253 
254 	default:
255 		break;
256 	}
257 
258 	// actually write the log message to the file now
259 	mLogFile.Write(mWriteBuf.String(), mWriteBuf.Length());
260 }
261