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