1/*
2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2010-2012, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
6
7#include "DebuggerInterface.h"
8
9#include <new>
10
11#include <stdio.h>
12
13#include <Locker.h>
14
15#include <AutoLocker.h>
16#include <commpage_defs.h>
17#include <OS.h>
18#include <system_info.h>
19#include <util/DoublyLinkedList.h>
20#include <util/KMessage.h>
21
22#include "debug_utils.h"
23
24#include "ArchitectureX86.h"
25#include "ArchitectureX8664.h"
26#include "CpuState.h"
27#include "DebugEvent.h"
28#include "ImageInfo.h"
29#include "SymbolInfo.h"
30#include "ThreadInfo.h"
31
32
33// number of debug contexts the pool does initially create
34static const int kInitialDebugContextCount = 3;
35
36// maximum number of debug contexts in the pool
37static const int kMaxDebugContextCount = 10;
38
39
40struct DebuggerInterface::DebugContext : debug_context,
41		DoublyLinkedListLinkImpl<DebugContext> {
42	DebugContext()
43	{
44		team = -1;
45		nub_port = -1;
46		reply_port = -1;
47	}
48
49	~DebugContext()
50	{
51		if (reply_port >= 0)
52			destroy_debug_context(this);
53	}
54
55	status_t Init(team_id team, port_id nubPort)
56	{
57		return init_debug_context(this, team, nubPort);
58	}
59
60	void Close()
61	{
62		if (reply_port >= 0) {
63			destroy_debug_context(this);
64			team = -1;
65			nub_port = -1;
66			reply_port = -1;
67		}
68	}
69};
70
71
72struct DebuggerInterface::DebugContextPool {
73	DebugContextPool(team_id team, port_id nubPort)
74		:
75		fLock("debug context pool"),
76		fTeam(team),
77		fNubPort(nubPort),
78		fBlockSem(-1),
79		fContextCount(0),
80		fWaiterCount(0),
81		fClosed(false)
82	{
83	}
84
85	~DebugContextPool()
86	{
87		AutoLocker<BLocker> locker(fLock);
88
89		while (DebugContext* context = fFreeContexts.RemoveHead())
90			delete context;
91
92		if (fBlockSem >= 0)
93			delete_sem(fBlockSem);
94	}
95
96	status_t Init()
97	{
98		status_t error = fLock.InitCheck();
99		if (error != B_OK)
100			return error;
101
102		fBlockSem = create_sem(0, "debug context pool block");
103		if (fBlockSem < 0)
104			return fBlockSem;
105
106		for (int i = 0; i < kInitialDebugContextCount; i++) {
107			DebugContext* context;
108			error = _CreateDebugContext(context);
109			if (error != B_OK)
110				return error;
111
112			fFreeContexts.Add(context);
113		}
114
115		return B_OK;
116	}
117
118	void Close()
119	{
120		AutoLocker<BLocker> locker(fLock);
121		fClosed = true;
122
123		for (DebugContextList::Iterator it = fFreeContexts.GetIterator();
124				DebugContext* context = it.Next();) {
125			context->Close();
126		}
127
128		for (DebugContextList::Iterator it = fUsedContexts.GetIterator();
129				DebugContext* context = it.Next();) {
130			context->Close();
131		}
132	}
133
134	DebugContext* GetContext()
135	{
136		AutoLocker<BLocker> locker(fLock);
137		DebugContext* context = fFreeContexts.RemoveHead();
138
139		if (context == NULL) {
140			if (fContextCount >= kMaxDebugContextCount
141				|| _CreateDebugContext(context) != B_OK) {
142				// wait for a free context
143				while (context == NULL) {
144					fWaiterCount++;
145					locker.Unlock();
146					while (acquire_sem(fBlockSem) != B_OK);
147					locker.Lock();
148					context = fFreeContexts.RemoveHead();
149				}
150			}
151		}
152
153		fUsedContexts.Add(context);
154
155		return context;
156	}
157
158	void PutContext(DebugContext* context)
159	{
160		AutoLocker<BLocker> locker(fLock);
161		fUsedContexts.Remove(context);
162		fFreeContexts.Add(context);
163
164		if (fWaiterCount > 0)
165			release_sem(fBlockSem);
166	}
167
168private:
169	typedef DoublyLinkedList<DebugContext> DebugContextList;
170
171private:
172	status_t _CreateDebugContext(DebugContext*& _context)
173	{
174		DebugContext* context = new(std::nothrow) DebugContext;
175		if (context == NULL)
176			return B_NO_MEMORY;
177
178		if (!fClosed) {
179			status_t error = context->Init(fTeam, fNubPort);
180			if (error != B_OK) {
181				delete context;
182				return error;
183			}
184		}
185
186		fContextCount++;
187
188		_context = context;
189		return B_OK;
190	}
191
192private:
193	BLocker				fLock;
194	team_id				fTeam;
195	port_id				fNubPort;
196	sem_id				fBlockSem;
197	int32				fContextCount;
198	int32				fWaiterCount;
199	DebugContextList	fFreeContexts;
200	DebugContextList	fUsedContexts;
201	bool				fClosed;
202};
203
204
205struct DebuggerInterface::DebugContextGetter {
206	DebugContextGetter(DebugContextPool* pool)
207		:
208		fPool(pool),
209		fContext(pool->GetContext())
210	{
211	}
212
213	~DebugContextGetter()
214	{
215		fPool->PutContext(fContext);
216	}
217
218	DebugContext* Context() const
219	{
220		return fContext;
221	}
222
223private:
224	DebugContextPool*	fPool;
225	DebugContext*		fContext;
226};
227
228// #pragma mark - DebuggerInterface
229
230DebuggerInterface::DebuggerInterface(team_id teamID)
231	:
232	fTeamID(teamID),
233	fDebuggerPort(-1),
234	fNubPort(-1),
235	fDebugContextPool(NULL),
236	fArchitecture(NULL)
237{
238}
239
240
241DebuggerInterface::~DebuggerInterface()
242{
243	if (fArchitecture != NULL)
244		fArchitecture->ReleaseReference();
245
246	Close(false);
247
248	delete fDebugContextPool;
249}
250
251
252status_t
253DebuggerInterface::Init()
254{
255	// create the architecture
256	// TODO: this probably needs to be rethought a bit,
257	// since especially when we eventually support remote debugging,
258	// the architecture will depend on the target machine, not the host
259#if defined(ARCH_x86)
260	fArchitecture = new(std::nothrow) ArchitectureX86(this);
261#elif defined(ARCH_x86_64)
262	fArchitecture = new(std::nothrow) ArchitectureX8664(this);
263#else
264	return B_UNSUPPORTED;
265#endif
266
267	if (fArchitecture == NULL)
268		return B_NO_MEMORY;
269
270	status_t error = fArchitecture->Init();
271	if (error != B_OK)
272		return error;
273
274	// create debugger port
275	char buffer[128];
276	snprintf(buffer, sizeof(buffer), "team %" B_PRId32 " debugger", fTeamID);
277	fDebuggerPort = create_port(100, buffer);
278	if (fDebuggerPort < 0)
279		return fDebuggerPort;
280
281	// install as team debugger
282	fNubPort = install_team_debugger(fTeamID, fDebuggerPort);
283	if (fNubPort < 0)
284		return fNubPort;
285
286	error = __start_watching_system(fTeamID, B_WATCH_SYSTEM_THREAD_PROPERTIES,
287		fDebuggerPort, 0);
288	if (error != B_OK)
289		return error;
290// TODO: Stop watching in Close()!
291
292	// create debug context pool
293	fDebugContextPool = new(std::nothrow) DebugContextPool(fTeamID, fNubPort);
294	if (fDebugContextPool == NULL)
295		return B_NO_MEMORY;
296
297	error = fDebugContextPool->Init();
298	if (error != B_OK)
299		return error;
300
301	return B_OK;
302}
303
304
305void
306DebuggerInterface::Close(bool killTeam)
307{
308	if (killTeam)
309		kill_team(fTeamID);
310	else if (fNubPort >= 0)
311		remove_team_debugger(fTeamID);
312
313	if (fDebuggerPort >= 0)
314		delete_port(fDebuggerPort);
315}
316
317
318status_t
319DebuggerInterface::GetNextDebugEvent(DebugEvent*& _event)
320{
321	while (true) {
322		char buffer[2048];
323		int32 messageCode;
324		ssize_t size = read_port(fDebuggerPort, &messageCode, buffer,
325			sizeof(buffer));
326		if (size < 0) {
327			if (size == B_INTERRUPTED)
328				continue;
329
330			return size;
331		}
332
333		if (messageCode <= B_DEBUGGER_MESSAGE_HANDED_OVER) {
334 			debug_debugger_message_data message;
335			memcpy(&message, buffer, size);
336			if (message.origin.team != fTeamID)
337				continue;
338
339			bool ignore = false;
340			status_t error = _CreateDebugEvent(messageCode, message, ignore,
341				_event);
342			if (error != B_OK)
343				return error;
344
345			if (ignore) {
346				if (message.origin.thread >= 0 && message.origin.nub_port >= 0)
347					continue_thread(message.origin.nub_port,
348						message.origin.thread);
349				continue;
350			}
351
352			return B_OK;
353		}
354
355		KMessage message;
356		size = message.SetTo(buffer);
357		if (size != B_OK)
358			return size;
359		return _GetNextSystemWatchEvent(_event, message);
360	}
361
362	return B_OK;
363}
364
365
366status_t
367DebuggerInterface::SetTeamDebuggingFlags(uint32 flags)
368{
369	set_team_debugging_flags(fNubPort, flags);
370	return B_OK;
371}
372
373
374status_t
375DebuggerInterface::ContinueThread(thread_id thread)
376{
377	continue_thread(fNubPort, thread);
378	return B_OK;
379}
380
381
382status_t
383DebuggerInterface::StopThread(thread_id thread)
384{
385	return debug_thread(thread);
386}
387
388
389status_t
390DebuggerInterface::SingleStepThread(thread_id thread)
391{
392	debug_nub_continue_thread continueMessage;
393	continueMessage.thread = thread;
394	continueMessage.handle_event = B_THREAD_DEBUG_HANDLE_EVENT;
395	continueMessage.single_step = true;
396
397	return write_port(fNubPort, B_DEBUG_MESSAGE_CONTINUE_THREAD,
398		&continueMessage, sizeof(continueMessage));
399}
400
401
402status_t
403DebuggerInterface::InstallBreakpoint(target_addr_t address)
404{
405	DebugContextGetter contextGetter(fDebugContextPool);
406
407	debug_nub_set_breakpoint message;
408	message.reply_port = contextGetter.Context()->reply_port;
409	message.address = (void*)(addr_t)address;
410
411	debug_nub_set_breakpoint_reply reply;
412
413	status_t error = send_debug_message(contextGetter.Context(),
414		B_DEBUG_MESSAGE_SET_BREAKPOINT, &message, sizeof(message), &reply,
415		sizeof(reply));
416	return error == B_OK ? reply.error : error;
417}
418
419
420status_t
421DebuggerInterface::UninstallBreakpoint(target_addr_t address)
422{
423	debug_nub_clear_breakpoint message;
424	message.address = (void*)(addr_t)address;
425
426	return write_port(fNubPort, B_DEBUG_MESSAGE_CLEAR_BREAKPOINT,
427		&message, sizeof(message));
428}
429
430
431status_t
432DebuggerInterface::InstallWatchpoint(target_addr_t address, uint32 type,
433	int32 length)
434{
435	DebugContextGetter contextGetter(fDebugContextPool);
436
437	debug_nub_set_watchpoint message;
438	message.reply_port = contextGetter.Context()->reply_port;
439	message.address = (void*)(addr_t)address;
440	message.type = type;
441	message.length = length;
442
443	debug_nub_set_watchpoint_reply reply;
444
445	status_t error = send_debug_message(contextGetter.Context(),
446		B_DEBUG_MESSAGE_SET_WATCHPOINT, &message, sizeof(message), &reply,
447		sizeof(reply));
448	return error == B_OK ? reply.error : error;
449}
450
451
452status_t
453DebuggerInterface::UninstallWatchpoint(target_addr_t address)
454{
455	DebugContextGetter contextGetter(fDebugContextPool);
456
457	debug_nub_clear_watchpoint message;
458	message.address = (void*)(addr_t)address;
459
460	return write_port(fNubPort, B_DEBUG_MESSAGE_CLEAR_WATCHPOINT,
461		&message, sizeof(message));
462}
463
464
465status_t
466DebuggerInterface::GetThreadInfos(BObjectList<ThreadInfo>& infos)
467{
468	thread_info threadInfo;
469	int32 cookie = 0;
470	while (get_next_thread_info(fTeamID, &cookie, &threadInfo) == B_OK) {
471		ThreadInfo* info = new(std::nothrow) ThreadInfo(threadInfo.team,
472			threadInfo.thread, threadInfo.name);
473		if (info == NULL || !infos.AddItem(info)) {
474			delete info;
475			return B_NO_MEMORY;
476		}
477	}
478
479	return B_OK;
480}
481
482
483status_t
484DebuggerInterface::GetImageInfos(BObjectList<ImageInfo>& infos)
485{
486	// get the team's images
487	image_info imageInfo;
488	int32 cookie = 0;
489	while (get_next_image_info(fTeamID, &cookie, &imageInfo) == B_OK) {
490		ImageInfo* info = new(std::nothrow) ImageInfo(fTeamID, imageInfo.id,
491			imageInfo.name, imageInfo.type, (addr_t)imageInfo.text,
492			imageInfo.text_size, (addr_t)imageInfo.data, imageInfo.data_size);
493		if (info == NULL || !infos.AddItem(info)) {
494			delete info;
495			return B_NO_MEMORY;
496		}
497	}
498
499	// Also add the "commpage" image, which belongs to the kernel, but is used
500	// by userland teams.
501	cookie = 0;
502	while (get_next_image_info(B_SYSTEM_TEAM, &cookie, &imageInfo) == B_OK) {
503		if ((addr_t)imageInfo.text >= USER_COMMPAGE_ADDR
504			&& (addr_t)imageInfo.text < USER_COMMPAGE_ADDR + COMMPAGE_SIZE) {
505			ImageInfo* info = new(std::nothrow) ImageInfo(B_SYSTEM_TEAM,
506				imageInfo.id, imageInfo.name, imageInfo.type,
507				(addr_t)imageInfo.text, imageInfo.text_size,
508				(addr_t)imageInfo.data, imageInfo.data_size);
509			if (info == NULL || !infos.AddItem(info)) {
510				delete info;
511				return B_NO_MEMORY;
512			}
513			break;
514		}
515	}
516
517	return B_OK;
518}
519
520
521status_t
522DebuggerInterface::GetSymbolInfos(team_id team, image_id image,
523	BObjectList<SymbolInfo>& infos)
524{
525	// create a lookup context
526// TODO: It's too expensive to create a lookup context for each image!
527	debug_symbol_lookup_context* lookupContext;
528	status_t error = debug_create_symbol_lookup_context(team, &lookupContext);
529	if (error != B_OK)
530		return error;
531
532	// create a symbol iterator
533	debug_symbol_iterator* iterator;
534	error = debug_create_image_symbol_iterator(
535		lookupContext, image, &iterator);
536	if (error != B_OK) {
537		debug_delete_symbol_lookup_context(lookupContext);
538		return error;
539	}
540
541	// get the symbols
542	char name[1024];
543	int32 type;
544	void* address;
545	size_t size;
546	while (debug_next_image_symbol(iterator, name, sizeof(name), &type,
547			&address, &size) == B_OK) {
548		SymbolInfo* info = new(std::nothrow) SymbolInfo(
549			(target_addr_t)(addr_t)address, size, type, name);
550		if (info == NULL)
551			break;
552		if (!infos.AddItem(info)) {
553			delete info;
554			break;
555		}
556	}
557
558	// delete the symbol iterator and lookup context
559	debug_delete_symbol_iterator(iterator);
560	debug_delete_symbol_lookup_context(lookupContext);
561
562	return B_OK;
563}
564
565
566status_t
567DebuggerInterface::GetSymbolInfo(team_id team, image_id image, const char* name,
568	int32 symbolType, SymbolInfo& info)
569{
570	// create a lookup context
571	// TODO: It's a bit expensive to create a lookup context just for one
572	// symbol!
573	debug_symbol_lookup_context* lookupContext;
574	status_t error = debug_create_symbol_lookup_context(team, &lookupContext);
575	if (error != B_OK)
576		return error;
577
578	// try to get the symbol
579	void* foundAddress;
580	size_t foundSize;
581	int32 foundType;
582	error = debug_get_symbol(lookupContext, image, name, symbolType,
583		&foundAddress, &foundSize, &foundType);
584	if (error == B_OK) {
585		info.SetTo((target_addr_t)(addr_t)foundAddress, foundSize, foundType,
586			name);
587	}
588
589	// delete the lookup context
590	debug_delete_symbol_lookup_context(lookupContext);
591
592	return error;
593}
594
595
596status_t
597DebuggerInterface::GetThreadInfo(thread_id thread, ThreadInfo& info)
598{
599	thread_info threadInfo;
600	status_t error = get_thread_info(thread, &threadInfo);
601	if (error != B_OK)
602		return error;
603
604	info.SetTo(threadInfo.team, threadInfo.thread, threadInfo.name);
605	return B_OK;
606}
607
608
609status_t
610DebuggerInterface::GetCpuState(thread_id thread, CpuState*& _state)
611{
612	DebugContextGetter contextGetter(fDebugContextPool);
613
614	debug_nub_get_cpu_state message;
615	message.reply_port = contextGetter.Context()->reply_port;
616	message.thread = thread;
617
618	debug_nub_get_cpu_state_reply reply;
619
620	status_t error = send_debug_message(contextGetter.Context(),
621		B_DEBUG_MESSAGE_GET_CPU_STATE, &message, sizeof(message), &reply,
622		sizeof(reply));
623	if (error != B_OK)
624		return error;
625	if (reply.error != B_OK)
626		return reply.error;
627
628	return fArchitecture->CreateCpuState(&reply.cpu_state,
629		sizeof(debug_cpu_state), _state);
630}
631
632
633ssize_t
634DebuggerInterface::ReadMemory(target_addr_t address, void* buffer, size_t size)
635{
636	DebugContextGetter contextGetter(fDebugContextPool);
637
638	return debug_read_memory(contextGetter.Context(),
639		(const void*)(addr_t)address, buffer, size);
640}
641
642
643ssize_t
644DebuggerInterface::WriteMemory(target_addr_t address, void* buffer,
645	size_t size)
646{
647	DebugContextGetter contextGetter(fDebugContextPool);
648
649	return debug_write_memory(contextGetter.Context(),
650		(const void*)(addr_t)address, buffer, size);
651}
652
653
654status_t
655DebuggerInterface::_CreateDebugEvent(int32 messageCode,
656	const debug_debugger_message_data& message, bool& _ignore,
657	DebugEvent*& _event)
658{
659	DebugEvent* event = NULL;
660
661	switch (messageCode) {
662		case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED:
663			event = new(std::nothrow) ThreadDebuggedEvent(message.origin.team,
664				message.origin.thread);
665			break;
666		case B_DEBUGGER_MESSAGE_DEBUGGER_CALL:
667			event = new(std::nothrow) DebuggerCallEvent(message.origin.team,
668				message.origin.thread,
669				(target_addr_t)message.debugger_call.message);
670			break;
671		case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT:
672		{
673			CpuState* state = NULL;
674			status_t error = fArchitecture->CreateCpuState(
675				&message.breakpoint_hit.cpu_state,
676				sizeof(debug_cpu_state), state);
677			if (error != B_OK)
678				return error;
679
680			event = new(std::nothrow) BreakpointHitEvent(message.origin.team,
681				message.origin.thread, state);
682			state->ReleaseReference();
683			break;
684		}
685		case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT:
686		{
687			CpuState* state = NULL;
688			status_t error = fArchitecture->CreateCpuState(
689				&message.watchpoint_hit.cpu_state,
690				sizeof(debug_cpu_state), state);
691			if (error != B_OK)
692				return error;
693
694			event = new(std::nothrow) WatchpointHitEvent(message.origin.team,
695				message.origin.thread, state);
696			state->ReleaseReference();
697			break;
698		}
699		case B_DEBUGGER_MESSAGE_SINGLE_STEP:
700		{
701			CpuState* state = NULL;
702			status_t error = fArchitecture->CreateCpuState(
703				&message.single_step.cpu_state,
704				sizeof(debug_cpu_state), state);
705			if (error != B_OK)
706				return error;
707
708			event = new(std::nothrow) SingleStepEvent(message.origin.team,
709				message.origin.thread, state);
710			state->ReleaseReference();
711			break;
712		}
713		case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED:
714			event = new(std::nothrow) ExceptionOccurredEvent(
715				message.origin.team, message.origin.thread,
716				message.exception_occurred.exception);
717			break;
718		case B_DEBUGGER_MESSAGE_TEAM_DELETED:
719			if (message.origin.team != fTeamID) {
720				_ignore = true;
721				return B_OK;
722			}
723			event = new(std::nothrow) TeamDeletedEvent(message.origin.team,
724				message.origin.thread);
725			break;
726		case B_DEBUGGER_MESSAGE_TEAM_EXEC:
727			if (message.origin.team != fTeamID) {
728				_ignore = true;
729				return B_OK;
730			}
731			event = new(std::nothrow) TeamExecEvent(message.origin.team,
732				message.origin.thread);
733			break;
734		case B_DEBUGGER_MESSAGE_THREAD_CREATED:
735			event = new(std::nothrow) ThreadCreatedEvent(message.origin.team,
736				message.origin.thread, message.thread_created.new_thread);
737			break;
738		case B_DEBUGGER_MESSAGE_THREAD_DELETED:
739			event = new(std::nothrow) ThreadDeletedEvent(message.origin.team,
740				message.origin.thread);
741			break;
742		case B_DEBUGGER_MESSAGE_IMAGE_CREATED:
743		{
744			const image_info& info = message.image_created.info;
745			event = new(std::nothrow) ImageCreatedEvent(message.origin.team,
746				message.origin.thread,
747				ImageInfo(fTeamID, info.id, info.name, info.type,
748					(addr_t)info.text, info.text_size, (addr_t)info.data,
749					info.data_size));
750			break;
751		}
752		case B_DEBUGGER_MESSAGE_IMAGE_DELETED:
753		{
754			const image_info& info = message.image_deleted.info;
755			event = new(std::nothrow) ImageDeletedEvent(message.origin.team,
756				message.origin.thread,
757				ImageInfo(fTeamID, info.id, info.name, info.type,
758					(addr_t)info.text, info.text_size, (addr_t)info.data,
759					info.data_size));
760			break;
761		}
762		default:
763			printf("DebuggerInterface for team %" B_PRId32 ": unknown message "
764				"from kernel: %" B_PRId32 "\n", fTeamID, messageCode);
765			// fall through...
766		case B_DEBUGGER_MESSAGE_TEAM_CREATED:
767		case B_DEBUGGER_MESSAGE_PRE_SYSCALL:
768		case B_DEBUGGER_MESSAGE_POST_SYSCALL:
769		case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED:
770		case B_DEBUGGER_MESSAGE_PROFILER_UPDATE:
771		case B_DEBUGGER_MESSAGE_HANDED_OVER:
772			_ignore = true;
773			return B_OK;
774	}
775
776	if (event == NULL)
777		return B_NO_MEMORY;
778
779	if (message.origin.thread >= 0 && message.origin.nub_port >= 0)
780		event->SetThreadStopped(true);
781
782	_ignore = false;
783	_event = event;
784
785	return B_OK;
786}
787
788
789status_t
790DebuggerInterface::_GetNextSystemWatchEvent(DebugEvent*& _event,
791	KMessage& message)
792{
793	status_t error = B_OK;
794	if (message.What() != B_SYSTEM_OBJECT_UPDATE)
795		return B_BAD_DATA;
796
797	int32 opcode = 0;
798	if (message.FindInt32("opcode", &opcode) != B_OK)
799		return B_BAD_DATA;
800
801	DebugEvent* event = NULL;
802	switch (opcode)
803	{
804		case B_THREAD_NAME_CHANGED:
805		{
806			int32 threadID = -1;
807			if (message.FindInt32("thread", &threadID) != B_OK)
808				break;
809
810			thread_info info;
811			error = get_thread_info(threadID, &info);
812			if (error != B_OK)
813				break;
814
815			event = new(std::nothrow) ThreadRenamedEvent(fTeamID,
816				threadID, threadID, info.name);
817			break;
818		}
819
820		default:
821		{
822			error = B_BAD_DATA;
823			break;
824		}
825	}
826
827	if (event != NULL)
828		_event = event;
829
830	return error;
831}
832