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