1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2012, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "TeamDebugInfo.h"
9
10#include <stdio.h>
11
12#include <new>
13
14#include <AutoDeleter.h>
15#include <AutoLocker.h>
16
17#include "Architecture.h"
18#include "DebuggerInterface.h"
19#include "DebuggerTeamDebugInfo.h"
20#include "DisassembledCode.h"
21#include "DwarfTeamDebugInfo.h"
22#include "FileManager.h"
23#include "FileSourceCode.h"
24#include "Function.h"
25#include "FunctionID.h"
26#include "ImageDebugInfo.h"
27#include "LocatableFile.h"
28#include "SourceFile.h"
29#include "SourceLanguage.h"
30#include "SpecificImageDebugInfo.h"
31#include "StringUtils.h"
32#include "Type.h"
33#include "TypeLookupConstraints.h"
34
35
36// #pragma mark - FunctionHashDefinition
37
38
39struct TeamDebugInfo::FunctionHashDefinition {
40	typedef const FunctionInstance*	KeyType;
41	typedef	Function				ValueType;
42
43	size_t HashKey(const FunctionInstance* key) const
44	{
45		// Instances without source file only equal themselves.
46		if (key->SourceFile() == NULL)
47			return (uint32)(addr_t)key;
48
49		uint32 hash = StringUtils::HashValue(key->Name());
50		hash = hash * 17 + (uint32)(addr_t)key->SourceFile();
51		SourceLocation location = key->GetSourceLocation();
52		hash = hash * 17 + location.Line();
53		hash = hash * 17 + location.Column();
54
55		return hash;
56	}
57
58	size_t Hash(const Function* value) const
59	{
60		return HashKey(value->FirstInstance());
61	}
62
63	bool Compare(const FunctionInstance* key, const Function* value) const
64	{
65		// source file must be the same
66		if (key->SourceFile() != value->SourceFile())
67			return false;
68
69		// Instances without source file only equal themselves.
70		if (key->SourceFile() == NULL)
71			return key == value->FirstInstance();
72
73		// Source location and function name must also match.
74		return key->GetSourceLocation() == value->GetSourceLocation()
75			&& key->Name() == value->Name();
76	}
77
78	Function*& GetLink(Function* value) const
79	{
80		return value->fNext;
81	}
82};
83
84
85// #pragma mark - SourceFileEntry
86
87
88struct TeamDebugInfo::SourceFileEntry {
89	SourceFileEntry(LocatableFile* sourceFile)
90		:
91		fSourceFile(sourceFile),
92		fSourceCode(NULL)
93	{
94		fSourceFile->AcquireReference();
95	}
96
97	~SourceFileEntry()
98	{
99		SetSourceCode(NULL);
100		fSourceFile->ReleaseReference();
101	}
102
103	status_t Init()
104	{
105		return B_OK;
106	}
107
108	LocatableFile* SourceFile() const
109	{
110		return fSourceFile;
111	}
112
113	FileSourceCode* GetSourceCode() const
114	{
115		return fSourceCode;
116	}
117
118	void SetSourceCode(FileSourceCode* sourceCode)
119	{
120		if (sourceCode == fSourceCode)
121			return;
122
123		if (fSourceCode != NULL)
124			fSourceCode->ReleaseReference();
125
126		fSourceCode = sourceCode;
127
128		if (fSourceCode != NULL)
129			fSourceCode->AcquireReference();
130	}
131
132
133	bool IsUnused() const
134	{
135		return fFunctions.IsEmpty();
136	}
137
138	status_t AddFunction(Function* function)
139	{
140		if (!fFunctions.BinaryInsert(function, &_CompareFunctions))
141			return B_NO_MEMORY;
142
143		return B_OK;
144	}
145
146	void RemoveFunction(Function* function)
147	{
148		int32 index = fFunctions.BinarySearchIndex(*function,
149			&_CompareFunctions);
150		if (index >= 0)
151			fFunctions.RemoveItemAt(index);
152	}
153
154	Function* FunctionAtLocation(const SourceLocation& location) const
155	{
156		int32 index = fFunctions.BinarySearchIndexByKey(location,
157			&_CompareLocationFunction);
158		if (index >= 0)
159			return fFunctions.ItemAt(index);
160
161		// No exact match, so we return the previous function which might still
162		// contain the location.
163		index = -index - 1;
164
165		if (index == 0)
166			return NULL;
167
168		return fFunctions.ItemAt(index - 1);
169	}
170
171	Function* FunctionAt(int32 index) const
172	{
173		return fFunctions.ItemAt(index);
174	}
175
176	Function* FunctionByName(const BString& name) const
177	{
178		// TODO: That's not exactly optimal.
179		for (int32 i = 0; Function* function = fFunctions.ItemAt(i); i++) {
180			if (name == function->Name())
181				return function;
182		}
183		return NULL;
184	}
185
186private:
187	typedef BObjectList<Function> FunctionList;
188
189private:
190	static int _CompareFunctions(const Function* a, const Function* b)
191	{
192		SourceLocation locationA = a->GetSourceLocation();
193		SourceLocation locationB = b->GetSourceLocation();
194
195		if (locationA < locationB)
196			return -1;
197
198		if (locationA != locationB )
199			return 1;
200
201		// if the locations match we still need to compare by name to be
202		// certain, since differently typed instantiations of template
203		// functions will have the same source file and location
204		return a->Name().Compare(b->Name());
205	}
206
207	static int _CompareLocationFunction(const SourceLocation* location,
208		const Function* function)
209	{
210		SourceLocation functionLocation = function->GetSourceLocation();
211
212		if (*location < functionLocation)
213			return -1;
214
215		return *location == functionLocation ? 0 : 1;
216	}
217
218private:
219	LocatableFile*		fSourceFile;
220	FileSourceCode*		fSourceCode;
221	FunctionList		fFunctions;
222
223public:
224	SourceFileEntry*	fNext;
225};
226
227
228// #pragma mark - SourceFileHashDefinition
229
230
231struct TeamDebugInfo::SourceFileHashDefinition {
232	typedef const LocatableFile*	KeyType;
233	typedef	SourceFileEntry			ValueType;
234
235	size_t HashKey(const LocatableFile* key) const
236	{
237		return (size_t)(addr_t)key;
238	}
239
240	size_t Hash(const SourceFileEntry* value) const
241	{
242		return HashKey(value->SourceFile());
243	}
244
245	bool Compare(const LocatableFile* key, const SourceFileEntry* value) const
246	{
247		return key == value->SourceFile();
248	}
249
250	SourceFileEntry*& GetLink(SourceFileEntry* value) const
251	{
252		return value->fNext;
253	}
254};
255
256
257// #pragma mark - TeamDebugInfo
258
259
260TeamDebugInfo::TeamDebugInfo(DebuggerInterface* debuggerInterface,
261	Architecture* architecture, FileManager* fileManager)
262	:
263	fLock("team debug info"),
264	fDebuggerInterface(debuggerInterface),
265	fArchitecture(architecture),
266	fFileManager(fileManager),
267	fSpecificInfos(10, true),
268	fFunctions(NULL),
269	fSourceFiles(NULL),
270	fTypeCache(NULL)
271{
272	fDebuggerInterface->AcquireReference();
273}
274
275
276TeamDebugInfo::~TeamDebugInfo()
277{
278	if (fTypeCache != NULL)
279		fTypeCache->ReleaseReference();
280
281	if (fSourceFiles != NULL) {
282		SourceFileEntry* entry = fSourceFiles->Clear(true);
283		while (entry != NULL) {
284			SourceFileEntry* next = entry->fNext;
285			delete entry;
286			entry = next;
287		}
288
289		delete fSourceFiles;
290	}
291
292	if (fFunctions != NULL) {
293		Function* function = fFunctions->Clear(true);
294		while (function != NULL) {
295			Function* next = function->fNext;
296			function->ReleaseReference();
297			function = next;
298		}
299
300		delete fFunctions;
301	}
302
303	fDebuggerInterface->ReleaseReference();
304}
305
306
307status_t
308TeamDebugInfo::Init()
309{
310	// check the lock
311	status_t error = fLock.InitCheck();
312	if (error != B_OK)
313		return error;
314
315	// create function hash table
316	fFunctions = new(std::nothrow) FunctionTable;
317	if (fFunctions == NULL)
318		return B_NO_MEMORY;
319
320	error = fFunctions->Init();
321	if (error != B_OK)
322		return error;
323
324	// create source file hash table
325	fSourceFiles = new(std::nothrow) SourceFileTable;
326	if (fSourceFiles == NULL)
327		return B_NO_MEMORY;
328
329	error = fSourceFiles->Init();
330	if (error != B_OK)
331		return error;
332
333	// create a type cache
334	fTypeCache = new(std::nothrow) GlobalTypeCache;
335	if (fTypeCache == NULL)
336		return B_NO_MEMORY;
337
338	error = fTypeCache->Init();
339	if (error != B_OK)
340		return error;
341
342	// Create specific infos for all types of debug info we support, in
343	// descending order of expressiveness.
344
345	// DWARF
346	DwarfTeamDebugInfo* dwarfInfo = new(std::nothrow) DwarfTeamDebugInfo(
347		fArchitecture, fDebuggerInterface, fFileManager, this, fTypeCache);
348	if (dwarfInfo == NULL || !fSpecificInfos.AddItem(dwarfInfo)) {
349		delete dwarfInfo;
350		return B_NO_MEMORY;
351	}
352
353	error = dwarfInfo->Init();
354	if (error != B_OK)
355		return error;
356
357	// debugger based info
358	DebuggerTeamDebugInfo* debuggerInfo
359		= new(std::nothrow) DebuggerTeamDebugInfo(fDebuggerInterface,
360			fArchitecture);
361	if (debuggerInfo == NULL || !fSpecificInfos.AddItem(debuggerInfo)) {
362		delete debuggerInfo;
363		return B_NO_MEMORY;
364	}
365
366	error = debuggerInfo->Init();
367	if (error != B_OK)
368		return error;
369
370	return B_OK;
371}
372
373
374status_t
375TeamDebugInfo::LookupTypeByName(const BString& name,
376	const TypeLookupConstraints& constraints, Type*& _type)
377{
378	return GetType(fTypeCache, name, constraints, _type);
379}
380
381status_t
382TeamDebugInfo::GetType(GlobalTypeCache* cache, const BString& name,
383	const TypeLookupConstraints& constraints, Type*& _type)
384{
385	// maybe the type is already cached
386	AutoLocker<GlobalTypeCache> cacheLocker(cache);
387	Type* type = cache->GetType(name, constraints);
388	if (type != NULL) {
389		type->AcquireReference();
390		_type = type;
391		return B_OK;
392	}
393
394	cacheLocker.Unlock();
395
396	// Clone the image list and get references to the images, so we can iterate
397	// through them without locking.
398	AutoLocker<BLocker> locker(fLock);
399
400	ImageList images;
401	for (int32 i = 0; ImageDebugInfo* imageDebugInfo = fImages.ItemAt(i); i++) {
402		if (images.AddItem(imageDebugInfo))
403			imageDebugInfo->AcquireReference();
404	}
405
406	locker.Unlock();
407
408	// get the type
409	status_t error = B_ENTRY_NOT_FOUND;
410	for (int32 i = 0; ImageDebugInfo* imageDebugInfo = images.ItemAt(i); i++) {
411		error = imageDebugInfo->GetType(cache, name, constraints, type);
412		if (error == B_OK) {
413			_type = type;
414			break;
415		}
416	}
417
418	// release the references
419	for (int32 i = 0; ImageDebugInfo* imageDebugInfo = images.ItemAt(i); i++)
420		imageDebugInfo->ReleaseReference();
421
422	return error;
423}
424
425
426status_t
427TeamDebugInfo::LoadImageDebugInfo(const ImageInfo& imageInfo,
428	LocatableFile* imageFile, ImageDebugInfo*& _imageDebugInfo)
429{
430	ImageDebugInfo* imageDebugInfo = new(std::nothrow) ImageDebugInfo(
431		imageInfo);
432	if (imageDebugInfo == NULL)
433		return B_NO_MEMORY;
434	ObjectDeleter<ImageDebugInfo> imageDebugInfoDeleter(imageDebugInfo);
435
436	for (int32 i = 0; SpecificTeamDebugInfo* specificTeamInfo
437			= fSpecificInfos.ItemAt(i); i++) {
438		SpecificImageDebugInfo* specificImageInfo;
439		status_t error = specificTeamInfo->CreateImageDebugInfo(imageInfo,
440			imageFile, specificImageInfo);
441		if (error == B_OK) {
442			if (!imageDebugInfo->AddSpecificInfo(specificImageInfo)) {
443				delete specificImageInfo;
444				return B_NO_MEMORY;
445			}
446		} else if (error == B_NO_MEMORY)
447			return error;
448				// fail only when out of memory
449	}
450
451	status_t error = imageDebugInfo->FinishInit();
452	if (error != B_OK)
453		return error;
454
455	_imageDebugInfo = imageDebugInfoDeleter.Detach();
456	return B_OK;
457}
458
459
460status_t
461TeamDebugInfo::LoadSourceCode(LocatableFile* file, FileSourceCode*& _sourceCode)
462{
463	AutoLocker<BLocker> locker(fLock);
464
465	// If we don't know the source file, there's nothing we can do.
466	SourceFileEntry* entry = fSourceFiles->Lookup(file);
467	if (entry == NULL)
468		return B_ENTRY_NOT_FOUND;
469
470	// the source might already be loaded
471	FileSourceCode* sourceCode = entry->GetSourceCode();
472	if (sourceCode != NULL) {
473		sourceCode->AcquireReference();
474		_sourceCode = sourceCode;
475		return B_OK;
476	}
477
478	// get the source language from some function's image debug info
479	Function* function = entry->FunctionAt(0);
480	if (function == NULL)
481		return B_ENTRY_NOT_FOUND;
482
483	FunctionDebugInfo* functionDebugInfo
484		= function->FirstInstance()->GetFunctionDebugInfo();
485	SourceLanguage* language;
486	status_t error = functionDebugInfo->GetSpecificImageDebugInfo()
487		->GetSourceLanguage(functionDebugInfo, language);
488	if (error != B_OK)
489		return error;
490	BReference<SourceLanguage> languageReference(language, true);
491
492	// no source code yet
493//	locker.Unlock();
494	// TODO: It would be nice to unlock here, but we need to iterate through
495	// the images below. We could clone the list, acquire references, and
496	// unlock. Then we have to compare the list with the then current list when
497	// we're done loading.
498
499	// load the source file
500	SourceFile* sourceFile;
501	error = fFileManager->LoadSourceFile(file, sourceFile);
502	if (error != B_OK)
503		return error;
504
505	// create the source code
506	sourceCode = new(std::nothrow) FileSourceCode(file, sourceFile, language);
507	sourceFile->ReleaseReference();
508	if (sourceCode == NULL)
509		return B_NO_MEMORY;
510	BReference<FileSourceCode> sourceCodeReference(sourceCode, true);
511
512	error = sourceCode->Init();
513	if (error != B_OK)
514		return error;
515
516	// Iterate through all images that know the source file and ask them to add
517	// information.
518	bool anyInfo = false;
519	for (int32 i = 0; ImageDebugInfo* imageDebugInfo = fImages.ItemAt(i); i++)
520		anyInfo |= imageDebugInfo->AddSourceCodeInfo(file, sourceCode) == B_OK;
521
522	if (!anyInfo)
523		return B_ENTRY_NOT_FOUND;
524
525	entry->SetSourceCode(sourceCode);
526
527	_sourceCode = sourceCodeReference.Detach();
528	return B_OK;
529}
530
531
532status_t
533TeamDebugInfo::DisassembleFunction(FunctionInstance* functionInstance,
534	DisassembledCode*& _sourceCode)
535{
536	// allocate a buffer for the function code
537	static const target_size_t kMaxBufferSize = 64 * 1024;
538	target_size_t bufferSize = std::min(functionInstance->Size(),
539		kMaxBufferSize);
540	void* buffer = malloc(bufferSize);
541	if (buffer == NULL)
542		return B_NO_MEMORY;
543	MemoryDeleter bufferDeleter(buffer);
544
545	// read the function code
546	FunctionDebugInfo* functionDebugInfo
547		= functionInstance->GetFunctionDebugInfo();
548	ssize_t bytesRead = functionDebugInfo->GetSpecificImageDebugInfo()
549		->ReadCode(functionInstance->Address(), buffer, bufferSize);
550	if (bytesRead < 0)
551		return bytesRead;
552
553	return fArchitecture->DisassembleCode(functionDebugInfo, buffer, bytesRead,
554		_sourceCode);
555}
556
557
558status_t
559TeamDebugInfo::AddImageDebugInfo(ImageDebugInfo* imageDebugInfo)
560{
561	AutoLocker<BLocker> locker(fLock);
562		// We have both locks now, so that for read-only access either lock
563		// suffices.
564
565	if (!fImages.AddItem(imageDebugInfo))
566		return B_NO_MEMORY;
567
568	// Match all of the image debug info's functions instances with functions.
569	BObjectList<SourceFileEntry> sourceFileEntries;
570	for (int32 i = 0;
571		FunctionInstance* instance = imageDebugInfo->FunctionAt(i); i++) {
572		// lookup the function or create it, if it doesn't exist yet
573		Function* function = fFunctions->Lookup(instance);
574		if (function != NULL) {
575// TODO: Also update possible user breakpoints in this function!
576			function->AddInstance(instance);
577			instance->SetFunction(function);
578
579			// The new image debug info might have additional information about
580			// the source file of the function, so remember the source file
581			// entry.
582			if (LocatableFile* sourceFile = function->SourceFile()) {
583				SourceFileEntry* entry = fSourceFiles->Lookup(sourceFile);
584				if (entry != NULL && entry->GetSourceCode() != NULL)
585					sourceFileEntries.AddItem(entry);
586			}
587		} else {
588			function = new(std::nothrow) Function;
589			if (function == NULL) {
590				RemoveImageDebugInfo(imageDebugInfo);
591				return B_NO_MEMORY;
592			}
593			function->AddInstance(instance);
594			instance->SetFunction(function);
595
596			status_t error = _AddFunction(function);
597				// Insert after adding the instance. Otherwise the function
598				// wouldn't be hashable/comparable.
599			if (error != B_OK) {
600				function->RemoveInstance(instance);
601				instance->SetFunction(NULL);
602				RemoveImageDebugInfo(imageDebugInfo);
603				return error;
604			}
605		}
606	}
607
608	// update the source files the image debug info knows about
609	for (int32 i = 0; SourceFileEntry* entry = sourceFileEntries.ItemAt(i);
610			i++) {
611		FileSourceCode* sourceCode = entry->GetSourceCode();
612		sourceCode->Lock();
613		if (imageDebugInfo->AddSourceCodeInfo(entry->SourceFile(),
614				sourceCode) == B_OK) {
615			// TODO: Notify interesting parties! Iterate through all functions
616			// for this source file?
617		}
618		sourceCode->Unlock();
619	}
620
621	return B_OK;
622}
623
624
625void
626TeamDebugInfo::RemoveImageDebugInfo(ImageDebugInfo* imageDebugInfo)
627{
628	AutoLocker<BLocker> locker(fLock);
629		// We have both locks now, so that for read-only access either lock
630		// suffices.
631
632	// Remove the functions from all of the image debug info's functions
633	// instances.
634	for (int32 i = 0;
635		FunctionInstance* instance = imageDebugInfo->FunctionAt(i); i++) {
636		if (Function* function = instance->GetFunction()) {
637// TODO: Also update possible user breakpoints in this function!
638			if (function->FirstInstance() == function->LastInstance()) {
639				// function unused -- remove it
640				// Note, that we have to remove it from the hash before removing
641				// the instance, since otherwise the function cannot be compared
642				// anymore.
643				_RemoveFunction(function);
644				function->ReleaseReference();
645					// The instance still has a reference.
646			}
647
648			function->RemoveInstance(instance);
649			instance->SetFunction(NULL);
650				// If this was the last instance, it will remove the last
651				// reference to the function.
652		}
653	}
654
655	// remove cached types from that image
656	fTypeCache->RemoveTypes(imageDebugInfo->GetImageInfo().ImageID());
657
658	fImages.RemoveItem(imageDebugInfo);
659}
660
661
662ImageDebugInfo*
663TeamDebugInfo::ImageDebugInfoByName(const char* name) const
664{
665	for (int32 i = 0; ImageDebugInfo* imageDebugInfo = fImages.ItemAt(i); i++) {
666		if (imageDebugInfo->GetImageInfo().Name() == name)
667			return imageDebugInfo;
668	}
669
670	return NULL;
671}
672
673
674Function*
675TeamDebugInfo::FunctionAtSourceLocation(LocatableFile* file,
676	const SourceLocation& location) const
677{
678	if (SourceFileEntry* entry = fSourceFiles->Lookup(file))
679		return entry->FunctionAtLocation(location);
680	return NULL;
681}
682
683
684Function*
685TeamDebugInfo::FunctionByID(FunctionID* functionID) const
686{
687	if (SourceFunctionID* sourceFunctionID
688			= dynamic_cast<SourceFunctionID*>(functionID)) {
689		// get the source file
690		LocatableFile* file = fFileManager->GetSourceFile(
691			sourceFunctionID->SourceFilePath());
692		if (file == NULL)
693			return NULL;
694		BReference<LocatableFile> fileReference(file, true);
695
696		if (SourceFileEntry* entry = fSourceFiles->Lookup(file))
697			return entry->FunctionByName(functionID->FunctionName());
698		return NULL;
699	}
700
701	ImageFunctionID* imageFunctionID
702		= dynamic_cast<ImageFunctionID*>(functionID);
703	if (imageFunctionID == NULL)
704		return NULL;
705
706	ImageDebugInfo* imageDebugInfo
707		= ImageDebugInfoByName(imageFunctionID->ImageName());
708	if (imageDebugInfo == NULL)
709		return NULL;
710
711	FunctionInstance* functionInstance = imageDebugInfo->FunctionByName(
712		functionID->FunctionName());
713	return functionInstance != NULL ? functionInstance->GetFunction() : NULL;
714}
715
716
717status_t
718TeamDebugInfo::_AddFunction(Function* function)
719{
720	// If the function refers to a source file, add it to the respective entry.
721	if (LocatableFile* sourceFile = function->SourceFile()) {
722		SourceFileEntry* entry = fSourceFiles->Lookup(sourceFile);
723		if (entry == NULL) {
724			// no entry for the source file yet -- create on
725			entry = new(std::nothrow) SourceFileEntry(sourceFile);
726			if (entry == NULL)
727				return B_NO_MEMORY;
728
729			status_t error = entry->Init();
730			if (error != B_OK) {
731				delete entry;
732				return error;
733			}
734
735			fSourceFiles->Insert(entry);
736		}
737
738		// add the function
739		status_t error = entry->AddFunction(function);
740		if (error != B_OK) {
741			if (entry->IsUnused()) {
742				fSourceFiles->Remove(entry);
743				delete entry;
744			}
745			return error;
746		}
747	}
748
749	fFunctions->Insert(function);
750
751	return B_OK;
752}
753
754
755void
756TeamDebugInfo::_RemoveFunction(Function* function)
757{
758	fFunctions->Remove(function);
759
760	// If the function refers to a source file, remove it from the respective
761	// entry.
762	if (LocatableFile* sourceFile = function->SourceFile()) {
763		if (SourceFileEntry* entry = fSourceFiles->Lookup(sourceFile))
764			entry->RemoveFunction(function);
765	}
766}
767