xref: /haiku/src/system/kernel/system_info.cpp (revision b46615c55ad2c8fe6de54412055a0713da3d610a)
1 /*
2  * Copyright (c) 2004-2010, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		Stefano Ceccherini
7  *		Axel Dörfler, axeld@pinc-software.de
8  *		Ingo Weinhold, ingo_weinhold@gmx.de
9  */
10 
11 
12 #include <ksystem_info.h>
13 #include <system_info.h>
14 #include <system_revision.h>
15 #include <arch/system_info.h>
16 
17 #include <string.h>
18 
19 #include <OS.h>
20 #include <KernelExport.h>
21 
22 #include <AutoDeleter.h>
23 
24 #include <block_cache.h>
25 #include <cpu.h>
26 #include <debug.h>
27 #include <kernel.h>
28 #include <lock.h>
29 #include <Notifications.h>
30 #include <messaging.h>
31 #include <port.h>
32 #include <real_time_clock.h>
33 #include <sem.h>
34 #include <smp.h>
35 #include <team.h>
36 #include <thread.h>
37 #include <util/AutoLock.h>
38 #include <vm/vm.h>
39 #include <vm/vm_page.h>
40 
41 
42 const static int64 kKernelVersion = 0x1;
43 const static char *kKernelName = "kernel_" HAIKU_ARCH;
44 
45 
46 static int
47 dump_info(int argc, char **argv)
48 {
49 	kprintf("kernel build: %s %s (gcc%d %s)\n", __DATE__, __TIME__, __GNUC__,
50 		__VERSION__);
51 	kprintf("revision: %s\n\n", get_haiku_revision());
52 
53 	kprintf("cpu count: %ld, active times:\n", smp_get_num_cpus());
54 
55 	for (int32 i = 0; i < smp_get_num_cpus(); i++)
56 		kprintf("  [%ld] %Ld\n", i + 1, gCPU[i].active_time);
57 
58 	// ToDo: Add page_faults
59 	kprintf("pages:\t\t%" B_PRIuPHYSADDR " (%" B_PRIuPHYSADDR " max)\n",
60 		vm_page_num_pages() - vm_page_num_free_pages(), vm_page_num_pages());
61 
62 	kprintf("sems:\t\t%ld (%ld max)\n", sem_used_sems(), sem_max_sems());
63 	kprintf("ports:\t\t%ld (%ld max)\n", port_used_ports(), port_max_ports());
64 	kprintf("threads:\t%ld (%ld max)\n", thread_used_threads(),
65 		thread_max_threads());
66 	kprintf("teams:\t\t%ld (%ld max)\n", team_used_teams(), team_max_teams());
67 
68 	return 0;
69 }
70 
71 
72 //	#pragma mark - user notifications
73 
74 
75 class SystemNotificationService : private NotificationListener {
76 public:
77 	SystemNotificationService()
78 	{
79 		mutex_init(&fLock, "system notification service");
80 	}
81 
82 	status_t Init()
83 	{
84 		status_t error = fTeamListeners.Init();
85 		if (error != B_OK)
86 			return error;
87 
88 		error = NotificationManager::Manager().AddListener("teams",
89 			TEAM_ADDED | TEAM_REMOVED | TEAM_EXEC, *this);
90 		if (error != B_OK)
91 			return error;
92 
93 		error = NotificationManager::Manager().AddListener("threads",
94 			THREAD_ADDED | THREAD_REMOVED | TEAM_EXEC, *this);
95 		if (error != B_OK)
96 			return error;
97 
98 		return B_OK;
99 	}
100 
101 	status_t StartListening(int32 object, uint32 flags, port_id port,
102 		int32 token)
103 	{
104 		// check the parameters
105 		if ((object < 0 && object != -1) || port < 0)
106 			return B_BAD_VALUE;
107 
108 		if ((flags & B_WATCH_SYSTEM_ALL) == 0
109 			|| (flags & ~(uint32)B_WATCH_SYSTEM_ALL) != 0) {
110 			return B_BAD_VALUE;
111 		}
112 
113 		MutexLocker locker(fLock);
114 
115 		// maybe the listener already exists
116 		ListenerList* listenerList;
117 		Listener* listener = _FindListener(object, port, token, listenerList);
118 		if (listener != NULL) {
119 			// just add the new flags
120 			listener->flags |= flags;
121 			return B_OK;
122 		}
123 
124 		// create a new listener
125 		listener = new(std::nothrow) Listener;
126 		if (listener == NULL)
127 			return B_NO_MEMORY;
128 		ObjectDeleter<Listener> listenerDeleter(listener);
129 
130 		listener->port = port;
131 		listener->token = token;
132 		listener->flags = flags;
133 
134 		// if there's no list yet, create a new list
135 		if (listenerList == NULL) {
136 			listenerList = new(std::nothrow) ListenerList;
137 			if (listenerList == NULL)
138 				return B_NO_MEMORY;
139 
140 			listenerList->object = object;
141 
142 			fTeamListeners.Insert(listenerList);
143 		}
144 
145 		listener->list = listenerList;
146 		listenerList->listeners.Add(listener);
147 		listenerDeleter.Detach();
148 
149 		team_associate_data(listener);
150 
151 		return B_OK;
152 	}
153 
154 	status_t StopListening(int32 object, uint32 flags, port_id port,
155 		int32 token)
156 	{
157 		MutexLocker locker(fLock);
158 
159 		// find the listener
160 		ListenerList* listenerList;
161 		Listener* listener = _FindListener(object, port, token, listenerList);
162 		if (listener == NULL)
163 			return B_ENTRY_NOT_FOUND;
164 
165 		// clear the given flags
166 		listener->flags &= ~flags;
167 
168 		if (listener->flags != 0)
169 			return B_OK;
170 
171 		team_dissociate_data(listener);
172 		_RemoveListener(listener);
173 
174 		return B_OK;
175 	}
176 
177 private:
178 	struct ListenerList;
179 
180 	struct Listener : AssociatedData {
181 		DoublyLinkedListLink<Listener>	listLink;
182 		ListenerList*					list;
183 		port_id							port;
184 		int32							token;
185 		uint32							flags;
186 
187 		virtual void OwnerDeleted(AssociatedDataOwner* owner);
188 	};
189 
190 	friend struct Listener;
191 
192 	struct ListenerList {
193 		typedef DoublyLinkedList<Listener,
194 			DoublyLinkedListMemberGetLink<Listener, &Listener::listLink> > List;
195 
196 		ListenerList*	hashNext;
197 		List			listeners;
198 		int32			object;
199 	};
200 
201 	struct ListenerHashDefinition {
202 		typedef int32			KeyType;
203 		typedef	ListenerList	ValueType;
204 
205 		size_t HashKey(int32 key) const
206 		{
207 			return key;
208 		}
209 
210 		size_t Hash(const ListenerList* value) const
211 		{
212 			return HashKey(value->object);
213 		}
214 
215 		bool Compare(int32 key, const ListenerList* value) const
216 		{
217 			return value->object == key;
218 		}
219 
220 		ListenerList*& GetLink(ListenerList* value) const
221 		{
222 			return value->hashNext;
223 		}
224 	};
225 
226 	typedef BOpenHashTable<ListenerHashDefinition> ListenerHash;
227 
228 private:
229 	virtual void EventOccurred(NotificationService& service,
230 		const KMessage* event)
231 	{
232 		MutexLocker locker(fLock);
233 
234 		int32 eventCode;
235 		int32 teamID;
236 		if (event->FindInt32("event", &eventCode) != B_OK
237 			|| event->FindInt32("team", &teamID) != B_OK) {
238 			return;
239 		}
240 
241 		int32 object;
242 		uint32 opcode;
243 		uint32 flags;
244 
245 		// translate the event
246 		if (event->What() == TEAM_MONITOR) {
247 			switch (eventCode) {
248 				case TEAM_ADDED:
249 					opcode = B_TEAM_CREATED;
250 					flags = B_WATCH_SYSTEM_TEAM_CREATION;
251 					break;
252 				case TEAM_REMOVED:
253 					opcode = B_TEAM_DELETED;
254 					flags = B_WATCH_SYSTEM_TEAM_DELETION;
255 					break;
256 				case TEAM_EXEC:
257 					opcode = B_TEAM_EXEC;
258 					flags = B_WATCH_SYSTEM_TEAM_CREATION
259 						| B_WATCH_SYSTEM_TEAM_DELETION;
260 					break;
261 				default:
262 					return;
263 			}
264 
265 			object = teamID;
266 		} else if (event->What() == THREAD_MONITOR) {
267 			if (event->FindInt32("thread", &object) != B_OK)
268 				return;
269 
270 			switch (eventCode) {
271 				case THREAD_ADDED:
272 					opcode = B_THREAD_CREATED;
273 					flags = B_WATCH_SYSTEM_THREAD_CREATION;
274 					break;
275 				case THREAD_REMOVED:
276 					opcode = B_THREAD_DELETED;
277 					flags = B_WATCH_SYSTEM_THREAD_DELETION;
278 					break;
279 				case THREAD_NAME_CHANGED:
280 					opcode = B_THREAD_NAME_CHANGED;
281 					flags = B_WATCH_SYSTEM_THREAD_PROPERTIES;
282 					break;
283 				default:
284 					return;
285 			}
286 		} else
287 			return;
288 
289 		// find matching listeners
290 		messaging_target targets[kMaxMessagingTargetCount];
291 		int32 targetCount = 0;
292 
293 		_AddTargets(fTeamListeners.Lookup(teamID), flags, targets,
294 			targetCount, object, opcode);
295 		_AddTargets(fTeamListeners.Lookup(-1), flags, targets, targetCount,
296 			object, opcode);
297 
298 		// send the message
299 		if (targetCount > 0)
300 			_SendMessage(targets, targetCount, object, opcode);
301 	}
302 
303 	void _AddTargets(ListenerList* listenerList, uint32 flags,
304 		messaging_target* targets, int32& targetCount, int32 object,
305 		uint32 opcode)
306 	{
307 		if (listenerList == NULL)
308 			return;
309 
310 		for (ListenerList::List::Iterator it
311 				= listenerList->listeners.GetIterator();
312 			Listener* listener = it.Next();) {
313 			if ((listener->flags & flags) == 0)
314 				continue;
315 
316 			// array is full -- need to flush it first
317 			if (targetCount == kMaxMessagingTargetCount) {
318 				_SendMessage(targets, targetCount, object, opcode);
319 				targetCount = 0;
320 			}
321 
322 			// add the listener
323 			targets[targetCount].port = listener->port;
324 			targets[targetCount++].token = listener->token;
325 		}
326 	}
327 
328 	void _SendMessage(messaging_target* targets, int32 targetCount,
329 		int32 object, uint32 opcode)
330 	{
331 		// prepare the message
332 		char buffer[128];
333 		KMessage message;
334 		message.SetTo(buffer, sizeof(buffer), B_SYSTEM_OBJECT_UPDATE);
335 		message.AddInt32("opcode", opcode);
336 		if (opcode < B_THREAD_CREATED)
337 			message.AddInt32("team", object);
338 		else
339 			message.AddInt32("thread", object);
340 
341 		// send it
342 		send_message(message.Buffer(), message.ContentSize(), targets,
343 			targetCount);
344 	}
345 
346 	Listener* _FindListener(int32 object, port_id port, int32 token,
347 		ListenerList*& _listenerList)
348 	{
349 		_listenerList = fTeamListeners.Lookup(object);
350 		if (_listenerList == NULL)
351 			return NULL;
352 
353 		for (ListenerList::List::Iterator it
354 				= _listenerList->listeners.GetIterator();
355 			Listener* listener = it.Next();) {
356 			if (listener->port == port && listener->token == token)
357 				return listener;
358 		}
359 
360 		return NULL;
361 	}
362 
363 	void _RemoveObsoleteListener(Listener* listener)
364 	{
365 		MutexLocker locker(fLock);
366 		_RemoveListener(listener);
367 	}
368 
369 	void _RemoveListener(Listener* listener)
370 	{
371 		// no flags anymore -- remove the listener
372 		ListenerList* listenerList = listener->list;
373 		listenerList->listeners.Remove(listener);
374 		listener->ReleaseReference();
375 
376 		if (listenerList->listeners.IsEmpty()) {
377 			// no listeners in the list anymore -- remove the list from the hash
378 			// table
379 			fTeamListeners.Remove(listenerList);
380 			delete listenerList;
381 		}
382 	}
383 
384 private:
385 	static const int32 kMaxMessagingTargetCount = 8;
386 
387 	mutex			fLock;
388 	ListenerHash	fTeamListeners;
389 };
390 
391 static SystemNotificationService sSystemNotificationService;
392 
393 
394 void
395 SystemNotificationService::Listener::OwnerDeleted(AssociatedDataOwner* owner)
396 {
397 	sSystemNotificationService._RemoveObsoleteListener(this);
398 }
399 
400 
401 //	#pragma mark -
402 
403 
404 status_t
405 _get_system_info(system_info *info, size_t size)
406 {
407 	if (size != sizeof(system_info))
408 		return B_BAD_VALUE;
409 
410 	memset(info, 0, sizeof(system_info));
411 
412 	info->boot_time = rtc_boot_time();
413 	info->cpu_count = smp_get_num_cpus();
414 
415 	for (int32 i = 0; i < info->cpu_count; i++)
416 		info->cpu_infos[i].active_time = cpu_get_active_time(i);
417 
418 	vm_page_get_stats(info);
419 	// TODO: Add page_faults
420 
421 	info->used_threads = thread_used_threads();
422 	info->max_threads = thread_max_threads();
423 	info->used_teams = team_used_teams();
424 	info->max_teams = team_max_teams();
425 	info->used_ports = port_used_ports();
426 	info->max_ports = port_max_ports();
427 	info->used_sems = sem_used_sems();
428 	info->max_sems = sem_max_sems();
429 
430 	info->kernel_version = kKernelVersion;
431 	strlcpy(info->kernel_name, kKernelName, B_FILE_NAME_LENGTH);
432 	strlcpy(info->kernel_build_date, __DATE__, B_OS_NAME_LENGTH);
433 	strlcpy(info->kernel_build_time, __TIME__, B_OS_NAME_LENGTH);
434 	info->abi = B_HAIKU_ABI;
435 
436 	// all other stuff is architecture specific
437 	return arch_get_system_info(info, size);
438 }
439 
440 
441 status_t
442 system_info_init(struct kernel_args *args)
443 {
444 	add_debugger_command("info", &dump_info, "System info");
445 
446 	return arch_system_info_init(args);
447 }
448 
449 
450 status_t
451 system_notifications_init()
452 {
453 	new (&sSystemNotificationService) SystemNotificationService;
454 
455 	status_t error = sSystemNotificationService.Init();
456 	if (error != B_OK) {
457 		panic("system_info_init(): Failed to init system notification service");
458 		return error;
459 	}
460 
461 	return B_OK;
462 }
463 
464 
465 //	#pragma mark -
466 
467 
468 status_t
469 _user_get_system_info(system_info *userInfo, size_t size)
470 {
471 	// The BeBook says get_system_info() always returns B_OK,
472 	// but that ain't true with invalid addresses
473 	if (userInfo == NULL || !IS_USER_ADDRESS(userInfo))
474 		return B_BAD_ADDRESS;
475 
476 	system_info info;
477 	status_t status = _get_system_info(&info, size);
478 	if (status == B_OK) {
479 		if (user_memcpy(userInfo, &info, sizeof(system_info)) < B_OK)
480 			return B_BAD_ADDRESS;
481 
482 		return B_OK;
483 	}
484 
485 	return status;
486 }
487 
488 
489 status_t
490 _user_get_system_info_etc(int32 id, void* userInfo, size_t size)
491 {
492 	if (userInfo == NULL || !IS_USER_ADDRESS(userInfo))
493 		return B_BAD_ADDRESS;
494 
495 	switch (id) {
496 		case B_MEMORY_INFO:
497 		{
498 			if (size < sizeof(system_memory_info))
499 				return B_BAD_VALUE;
500 
501 			system_memory_info info;
502 			vm_get_info(&info);
503 
504 			info.block_cache_memory = block_cache_used_memory();
505 
506 			return user_memcpy(userInfo, &info, sizeof(system_memory_info));
507 		}
508 
509 		default:
510 			return B_BAD_VALUE;
511 	}
512 }
513 
514 
515 status_t
516 _user_start_watching_system(int32 object, uint32 flags, port_id port,
517 	int32 token)
518 {
519 	return sSystemNotificationService.StartListening(object, flags, port,
520 		token);
521 }
522 
523 
524 status_t
525 _user_stop_watching_system(int32 object, uint32 flags, port_id port,
526 	int32 token)
527 {
528 	return sSystemNotificationService.StopListening(object, flags, port, token);
529 }
530