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