2 * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 * Distributed under the terms of the MIT License.
7 #include <KernelExport.h>
9 #include <util/kernel_cpp.h>
11 #include <file_cache.h>
13 #include <generic_syscall.h>
22 //#define TRACE_CACHE_MODULE
23 #ifdef TRACE_CACHE_MODULE
24 # define TRACE(x) dprintf x
30 // generic syscall interface
31 #define CACHE_LOG_SYSCALLS "cache_log"
32 #define CACHE_LOG_SET_MODULE 1
37 char team_name
[B_OS_NAME_LENGTH
];
42 char file_name
[B_FILE_NAME_LENGTH
];
55 static const bigtime_t kLogTimeout
= 50000; // 50 ms
56 static const int32 kLogWriterFrequency
= 1; // every tenth second
57 static const uint32 kNumLogEntries
= 6000;
58 static const uint32 kLogWriteThreshold
= 2000;
60 static struct cache_log sLogEntries
[kNumLogEntries
];
61 static uint32 sCurrentEntry
;
62 static sem_id sLogEntrySem
;
63 static struct mutex sLock
;
65 static struct cache_module_info
*sCacheModule
;
69 put_log_entry(cache_log
*log
)
78 if (acquire_sem_etc(sLogEntrySem
, 1, B_RELATIVE_TIMEOUT
, kLogTimeout
) != B_OK
) {
79 dprintf("log: Dropped log entry!\n");
84 cache_log
*log
= &sLogEntries
[sCurrentEntry
++];
86 Thread
*thread
= thread_get_current_thread();
87 log
->team
= thread
->team
->id
;
88 strlcpy(log
->team_name
, thread
->name
, B_OS_NAME_LENGTH
);
90 log
->timestamp
= system_time();
97 log_node_opened(void *vnode
, int32 fdType
, mount_id device
, vnode_id parent
,
98 vnode_id node
, const char *name
, off_t size
)
100 cache_log
*log
= get_log_entry();
105 strlcpy(log
->file_name
, name
, B_FILE_NAME_LENGTH
);
107 log
->file_name
[0] = '\0';
108 vfs_get_vnode_name(vnode
, log
->file_name
, B_FILE_NAME_LENGTH
);
113 log
->device
= device
;
114 log
->parent
= parent
;
116 log
->timestamp
= system_time();
118 TRACE(("log: added entry %s, <%c%d> %ld:%Ld:%Ld:%s\n", log
->team_name
, log
->action
,
119 log
->type
, device
, parent
, node
, log
->file_name
));
123 if (sCacheModule
!= NULL
&& sCacheModule
->node_opened
!= NULL
)
124 sCacheModule
->node_opened(vnode
, fdType
, device
, parent
, node
, name
, size
);
129 log_node_closed(void *vnode
, int32 fdType
, mount_id device
, vnode_id node
, int32 accessType
)
131 cache_log
*log
= get_log_entry();
138 log
->device
= device
;
142 log
->access_type
= accessType
;
144 TRACE(("log: added entry %s, <c%d> %ld:%Ld, %ld\n",
145 log
->team_name
, log
->type
, device
, node
, accessType
));
149 if (sCacheModule
!= NULL
&& sCacheModule
->node_closed
!= NULL
)
150 sCacheModule
->node_closed(vnode
, fdType
, device
, node
, accessType
);
155 log_node_launched(size_t argCount
, char * const *args
)
157 cache_log
*log
= get_log_entry();
162 log
->type
= FDTYPE_FILE
;
164 log
->launch
.args
= (char **)malloc(argCount
* sizeof(char *));
165 log
->launch
.arg_count
= argCount
;
167 for (uint32 i
= 0; i
< argCount
; i
++) {
169 // cut off path from parent team name
170 Team
*team
= thread_get_current_thread()->team
;
171 char name
[B_OS_NAME_LENGTH
];
174 state
= disable_interrupts();
177 log
->launch
.parent
= team
->parent
->id
;
178 strlcpy(name
, team
->parent
->main_thread
->name
, B_OS_NAME_LENGTH
);
181 restore_interrupts(state
);
183 log
->launch
.args
[0] = strdup(name
);
185 log
->launch
.args
[i
] = strdup(args
[i
]);
187 // remove newlines from the arguments
189 while ((newline
= strchr(log
->launch
.args
[i
], '\n')) != NULL
) {
194 if (vfs_get_cwd(&log
->device
, &log
->parent
) != B_OK
) {
199 TRACE(("log: added entry %s, <l> %ld:%Ld %s ...\n", log
->team_name
,
200 log
->device
, log
->parent
, args
[0]));
204 if (sCacheModule
!= NULL
&& sCacheModule
->node_launched
!= NULL
)
205 sCacheModule
->node_launched(argCount
, args
);
210 log_control(const char *subsystem
, uint32 function
,
211 void *buffer
, size_t bufferSize
)
214 case CACHE_LOG_SET_MODULE
:
216 cache_module_info
*module
= sCacheModule
;
218 // unset previous module
220 if (sCacheModule
!= NULL
) {
222 snooze(100000); // 0.1 secs
223 put_module(module
->info
.name
);
226 // get new module, if any
231 char name
[B_FILE_NAME_LENGTH
];
232 if (!IS_USER_ADDRESS(buffer
)
233 || user_strlcpy(name
, (char *)buffer
, B_FILE_NAME_LENGTH
) < B_OK
)
234 return B_BAD_ADDRESS
;
236 if (strncmp(name
, CACHE_MODULES_NAME
, strlen(CACHE_MODULES_NAME
)))
239 TRACE(("log_control: set module %s!\n", name
));
241 status_t status
= get_module(name
, (module_info
**)&module
);
243 sCacheModule
= module
;
249 return B_BAD_HANDLER
;
254 log_writer_daemon(void *arg
, int /*iteration*/)
258 if (sCurrentEntry
> kLogWriteThreshold
|| arg
!= NULL
) {
261 if (fstat(sLogFile
, &stat
) == 0) {
262 // enlarge file, so that it can be written faster
263 fileSize
= stat
.st_size
;
264 ftruncate(sLogFile
, fileSize
+ 512 * 1024);
268 for (uint32 i
= 0; i
< sCurrentEntry
; i
++) {
269 cache_log
*log
= &sLogEntries
[i
];
273 switch (log
->action
) {
275 length
= snprintf(line
, sizeof(line
), "%ld: %Ld \"%s\" l %ld:%Ld %ld \"%s\" ",
276 log
->team
, log
->timestamp
, log
->team_name
,
277 log
->device
, log
->parent
, log
->launch
.parent
,
278 log
->launch
.args
[0]);
279 length
= std::min(length
, (ssize_t
)sizeof(line
) - 1);
281 for (int32 j
= 1; j
< log
->launch
.arg_count
; j
++) {
282 // write argument list one by one, so that we can deal
283 // with very long argument lists
284 ssize_t written
= write(sLogFile
, line
, length
);
285 if (written
!= length
) {
286 dprintf("log: must drop log entries: %ld, %s!\n",
287 written
, strerror(written
));
292 strlcpy(line
, log
->launch
.args
[j
], sizeof(line
));
293 length
= strlcat(line
, "\xb0 ", sizeof(line
));
296 if (length
>= (ssize_t
)sizeof(line
))
297 length
= sizeof(line
) - 1;
299 line
[length
- 1] = '\n';
303 length
= snprintf(line
, sizeof(line
), "%ld: %Ld \"%s\" c%d %ld:%Ld %ld\n",
304 log
->team
, log
->timestamp
, log
->team_name
, log
->type
,
305 log
->device
, log
->node
, log
->access_type
);
306 length
= std::min(length
, (ssize_t
)sizeof(line
) - 1);
310 length
= snprintf(line
, sizeof(line
), "%ld: %Ld \"%s\" %c%d %ld:%Ld:%Ld:\"%s\"\n",
311 log
->team
, log
->timestamp
, log
->team_name
, log
->action
, log
->type
, log
->device
,
312 log
->parent
, log
->node
, log
->file_name
);
313 length
= std::min(length
, (ssize_t
)sizeof(line
) - 1);
317 ssize_t written
= write(sLogFile
, line
, length
);
318 if (written
!= length
) {
319 dprintf("log: must drop log entries: %ld, %s!\n", written
, strerror(written
));
325 // shrink file again to its real size
326 if (stat
.st_size
!= -1)
327 ftruncate(sLogFile
, fileSize
);
329 // need to free any launch log items (also if writing fails)
331 for (uint32 i
= 0; i
< sCurrentEntry
; i
++) {
332 cache_log
*log
= &sLogEntries
[i
];
333 if (log
->action
!= 'l')
336 for (int32 j
= 0; j
< log
->launch
.arg_count
; j
++)
337 free(log
->launch
.args
[j
]);
339 free(log
->launch
.args
);
342 release_sem_etc(sLogEntrySem
, sCurrentEntry
, B_DO_NOT_RESCHEDULE
);
346 mutex_unlock(&sLock
);
353 TRACE(("** log uninit - \n"));
355 unregister_kernel_daemon(log_writer_daemon
, NULL
);
356 unregister_generic_syscall(CACHE_LOG_SYSCALLS
, 1);
358 log_writer_daemon((void *)true, 0);
361 delete_sem(sLogEntrySem
);
362 mutex_destroy(&sLock
);
365 if (sCacheModule
!= NULL
)
366 put_module(sCacheModule
->info
.name
);
368 TRACE(("** - log uninit\n"));
375 TRACE(("** log init\n"));
379 char name
[B_FILE_NAME_LENGTH
];
380 snprintf(name
, sizeof(name
), "/var/log/cache_%03ld", number
++);
382 sLogFile
= open(name
, O_WRONLY
| O_CREAT
| O_EXCL
, DEFFILEMODE
);
383 } while (sLogFile
< 0 && errno
== B_FILE_EXISTS
);
385 if (sLogFile
< B_OK
) {
386 dprintf("log: opening log file failed: %s\n", strerror(errno
));
390 sLogEntrySem
= create_sem(kNumLogEntries
, "cache log entries");
391 if (sLogEntrySem
>= B_OK
) {
392 mutex_init(&sLock
, "log cache module");
393 register_kernel_daemon(log_writer_daemon
, NULL
, kLogWriterFrequency
);
394 register_generic_syscall(CACHE_LOG_SYSCALLS
, log_control
, 1, 0);
396 TRACE(("** - log init\n"));
406 log_std_ops(int32 op
, ...)
412 case B_MODULE_UNINIT
:
422 static struct cache_module_info sLogCacheModule
= {
424 CACHE_MODULES_NAME
"/log/v1",
434 module_info
*modules
[] = {
435 (module_info
*)&sLogCacheModule
,