1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5#ifndef ABSTRACT_WAIT_OBJECTS_PAGE_H
6#define ABSTRACT_WAIT_OBJECTS_PAGE_H
7
8
9#include <stdio.h>
10
11#include <new>
12
13#include <CheckBox.h>
14#include <GroupView.h>
15
16#include "model/Model.h"
17#include "table/TableColumns.h"
18#include "table/TreeTable.h"
19
20
21#define ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE						\
22	template<typename ModelType, typename WaitObjectGroupType,	\
23		typename WaitObjectType>
24#define ABSTRACT_WAIT_OBJECTS_PAGE_CLASS	\
25	AbstractWaitObjectsPage<ModelType, WaitObjectGroupType,	WaitObjectType>
26
27
28ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
29class AbstractWaitObjectsPage : public BGroupView, protected TreeTableListener {
30public:
31								AbstractWaitObjectsPage();
32	virtual						~AbstractWaitObjectsPage();
33
34			void				SetModel(ModelType* model);
35
36	virtual	void				MessageReceived(BMessage* message);
37
38	virtual	void				AttachedToWindow();
39
40protected:
41			class WaitObjectsTreeModel;
42
43			enum {
44				MSG_ABSTRACT_WAIT_OBJECTS_GROUP_BY_NAME		= 'awog'
45			};
46
47protected:
48			void				_UpdateTreeModel();
49
50protected:
51			TreeTable*			fWaitObjectsTree;
52			WaitObjectsTreeModel* fWaitObjectsTreeModel;
53			ModelType*			fModel;
54			BCheckBox*			fGroupByNameCheckBox;
55			bool				fGroupByName;
56
57
58// #pragma mark - WaitObjectsTreeModel (inner class)
59
60
61class WaitObjectsTreeModel : public TreeTableModel {
62public:
63	WaitObjectsTreeModel(ModelType* model, bool groupByName)
64		:
65		fModel(model),
66		fRootNode(NULL)
67	{
68		fRootNode = new RootNode(fModel, groupByName);
69	}
70
71	~WaitObjectsTreeModel()
72	{
73		delete fRootNode;
74	}
75
76	virtual void* Root() const
77	{
78		return fRootNode;
79	}
80
81	virtual int32 CountChildren(void* parent) const
82	{
83		return ((Node*)parent)->CountChildren();
84	}
85
86	virtual void* ChildAt(void* parent, int32 index) const
87	{
88		return ((Node*)parent)->ChildAt(index);
89	}
90
91	virtual int32 CountColumns() const
92	{
93		return 6;
94	}
95
96	virtual bool GetValueAt(void* object, int32 columnIndex, BVariant& value)
97	{
98		return ((Node*)object)->GetValueAt(columnIndex, value);
99	}
100
101private:
102	struct Node {
103		virtual ~Node() {}
104
105		virtual const char* Name() const = 0;
106		virtual uint32 Type() const = 0;
107		virtual addr_t Object() const = 0;
108		virtual addr_t ReferencedObject() const = 0;
109		virtual int64 Waits() const = 0;
110		virtual nanotime_t TotalWaitTime() const = 0;
111
112		virtual int32 CountChildren() const = 0;
113		virtual void* ChildAt(int32 index) const = 0;
114
115		static int CompareByName(const Node* a, const Node* b)
116		{
117			int cmp = (int)a->Type() - (int)b->Type();
118			return cmp == 0 ? strcmp(a->Name(), b->Name()) : cmp;
119		}
120
121		bool GetValueAt(int32 columnIndex, BVariant& value)
122		{
123			switch (columnIndex) {
124				case 0:
125					value.SetTo(wait_object_type_name(Type()),
126						B_VARIANT_DONT_COPY_DATA);
127					return true;
128				case 1:
129					value.SetTo(Name(), B_VARIANT_DONT_COPY_DATA);
130					return true;
131				case 2:
132				{
133					addr_t object = Object();
134					if (object == 0)
135						return false;
136
137					char buffer[16];
138					snprintf(buffer, sizeof(buffer), "%#lx", object);
139					value.SetTo(buffer);
140					return true;
141				}
142				case 3:
143				{
144					addr_t object = ReferencedObject();
145					if (object == 0)
146						return false;
147
148					char buffer[16];
149					snprintf(buffer, sizeof(buffer), "%#lx", object);
150					value.SetTo(buffer);
151					return true;
152				}
153				case 4:
154					value.SetTo(Waits());
155					return true;
156				case 5:
157					value.SetTo(TotalWaitTime());
158					return true;
159				default:
160					return false;
161			}
162		}
163	};
164
165	struct ObjectNode : Node {
166		WaitObjectType* object;
167
168		ObjectNode(WaitObjectType* object)
169			:
170			object(object)
171		{
172		}
173
174		virtual const char* Name() const
175		{
176			return object->Name();
177		}
178
179		virtual uint32 Type() const
180		{
181			return object->Type();
182		}
183
184		virtual addr_t Object() const
185		{
186			return object->Object();
187		}
188
189		virtual addr_t ReferencedObject() const
190		{
191			return object->ReferencedObject();
192		}
193
194		virtual int64 Waits() const
195		{
196			return object->Waits();
197		}
198
199		virtual nanotime_t TotalWaitTime() const
200		{
201			return object->TotalWaitTime();
202		}
203
204		virtual int32 CountChildren() const
205		{
206			return 0;
207		}
208
209		virtual void* ChildAt(int32 index) const
210		{
211			return NULL;
212		}
213	};
214
215	// For GCC 2
216	friend struct ObjectNode;
217
218	struct GroupNode : Node {
219		WaitObjectGroupType*	group;
220		BObjectList<ObjectNode>	objectNodes;
221
222		GroupNode(WaitObjectGroupType* group)
223			:
224			group(group)
225		{
226			int32 count = group->CountWaitObjects();
227			for (int32 i = 0; i < count; i++) {
228				WaitObjectType* waitObject = group->WaitObjectAt(i);
229				if (!objectNodes.AddItem(new ObjectNode(waitObject)))
230					throw std::bad_alloc();
231			}
232		}
233
234		virtual const char* Name() const
235		{
236			return group->Name();
237		}
238
239		virtual uint32 Type() const
240		{
241			return group->Type();
242		}
243
244		virtual addr_t Object() const
245		{
246			return group->Object();
247		}
248
249		virtual addr_t ReferencedObject() const
250		{
251			return 0;
252		}
253
254		virtual int64 Waits() const
255		{
256			return group->Waits();
257		}
258
259		virtual nanotime_t TotalWaitTime() const
260		{
261			return group->TotalWaitTime();
262		}
263
264		virtual int32 CountChildren() const
265		{
266			return objectNodes.CountItems();
267		}
268
269		virtual void* ChildAt(int32 index) const
270		{
271			return objectNodes.ItemAt(index);
272		}
273	};
274
275	// For GCC 2
276	friend struct GroupNode;
277
278	struct NodeContainerNode : Node {
279		NodeContainerNode()
280			:
281			fChildren(20, true)
282		{
283		}
284
285		NodeContainerNode(BObjectList<Node>& nodes)
286			:
287			fChildren(20, true)
288		{
289			fChildren.AddList(&nodes);
290
291			// compute total waits and total wait time
292			fWaits = 0;
293			fTotalWaitTime = 0;
294
295			for (int32 i = 0; Node* node = fChildren.ItemAt(i); i++) {
296				fWaits += node->Waits();
297				fTotalWaitTime += node->TotalWaitTime();
298			}
299		}
300
301		virtual const char* Name() const
302		{
303			Node* child = fChildren.ItemAt(0);
304			return child != NULL ? child->Name() : "";
305		}
306
307		virtual uint32 Type() const
308		{
309			Node* child = fChildren.ItemAt(0);
310			return child != NULL ? child->Type() : 0;
311		}
312
313		virtual addr_t Object() const
314		{
315			return 0;
316		}
317
318		virtual addr_t ReferencedObject() const
319		{
320			return 0;
321		}
322
323		virtual int64 Waits() const
324		{
325			return fWaits;
326		}
327
328		virtual nanotime_t TotalWaitTime() const
329		{
330			return fTotalWaitTime;
331		}
332
333		virtual int32 CountChildren() const
334		{
335			return fChildren.CountItems();
336		}
337
338		virtual void* ChildAt(int32 index) const
339		{
340			return fChildren.ItemAt(index);
341		}
342
343	protected:
344		BObjectList<Node>	fChildren;
345		int64				fWaits;
346		nanotime_t			fTotalWaitTime;
347	};
348
349	struct RootNode : public NodeContainerNode {
350		ModelType*			model;
351
352		RootNode(ModelType* model, bool groupByName)
353			:
354			model(model)
355		{
356			// create nodes for the wait object groups
357			BObjectList<Node> tempChildren;
358			BObjectList<Node>& children
359				= groupByName ? tempChildren : NodeContainerNode::fChildren;
360			int32 count = model->CountWaitObjectGroups();
361			for (int32 i = 0; i < count; i++) {
362				WaitObjectGroupType* group = model->WaitObjectGroupAt(i);
363				if (!children.AddItem(_CreateGroupNode(group)))
364					throw std::bad_alloc();
365			}
366
367			// If we shall group the nodes by name, we create grouping nodes.
368			if (groupByName) {
369				if (children.CountItems() < 2) {
370					NodeContainerNode::fChildren.AddList(&children);
371					return;
372				}
373
374				// sort the nodes by name
375				children.SortItems(&Node::CompareByName);
376
377				// create groups
378				int32 nodeCount = children.CountItems();
379				BObjectList<Node> nameGroup;
380				Node* previousNode = children.ItemAt(0);
381				int32 groupNodeIndex = 0;
382				for (int32 i = 1; i < nodeCount; i++) {
383					Node* node = children.ItemAt(i);
384					if (strcmp(node->Name(), previousNode->Name())) {
385						// create the group -- or just add the node, if it's
386						// the only one
387						if (nameGroup.CountItems() > 1) {
388							NodeContainerNode::fChildren.AddItem(
389								new NodeContainerNode(nameGroup));
390						} else
391							NodeContainerNode::fChildren.AddItem(previousNode);
392
393						nameGroup.MakeEmpty();
394						groupNodeIndex = i;
395					}
396
397					// add the node
398					nameGroup.AddItem(node);
399
400					previousNode = node;
401				}
402			}
403		}
404
405	private:
406		static Node* _CreateGroupNode(WaitObjectGroupType* group)
407		{
408			// If the group has only one object, just create an object node.
409			if (group->CountWaitObjects() == 1)
410				return new ObjectNode(group->WaitObjectAt(0));
411
412			return new GroupNode(group);
413		}
414	};
415
416private:
417	ModelType*	fModel;
418	RootNode*	fRootNode;
419};	// WaitObjectsTreeModel
420
421};	// AbstractWaitObjectsPage
422
423
424// #pragma mark - WaitObjectsPage
425
426
427ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
428ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::AbstractWaitObjectsPage()
429	:
430	BGroupView(B_VERTICAL, 10),
431	fWaitObjectsTree(NULL),
432	fWaitObjectsTreeModel(NULL),
433	fModel(NULL),
434	fGroupByNameCheckBox(NULL),
435	fGroupByName(false)
436{
437	SetName("Wait objects");
438
439	fWaitObjectsTree = new TreeTable("wait object list", 0);
440	AddChild(fWaitObjectsTree->ToView());
441
442	fGroupByNameCheckBox = new BCheckBox("group by name checkbox",
443		"Group by name", new BMessage(MSG_ABSTRACT_WAIT_OBJECTS_GROUP_BY_NAME));
444	fGroupByNameCheckBox->SetValue(
445		fGroupByName ? B_CONTROL_ON : B_CONTROL_OFF);
446	AddChild(fGroupByNameCheckBox);
447
448	fWaitObjectsTree->AddColumn(new StringTableColumn(0, "Type", 80, 40, 1000,
449		B_TRUNCATE_END, B_ALIGN_LEFT));
450	fWaitObjectsTree->AddColumn(new StringTableColumn(1, "Name", 80, 40, 1000,
451		B_TRUNCATE_END, B_ALIGN_LEFT));
452	fWaitObjectsTree->AddColumn(new StringTableColumn(2, "Object", 80, 40, 1000,
453		B_TRUNCATE_END, B_ALIGN_LEFT));
454	fWaitObjectsTree->AddColumn(new StringTableColumn(3, "Referenced", 80, 40,
455		1000, B_TRUNCATE_END, B_ALIGN_LEFT));
456	fWaitObjectsTree->AddColumn(new Int64TableColumn(4, "Waits", 80, 20,
457		1000, B_TRUNCATE_END, B_ALIGN_RIGHT));
458	fWaitObjectsTree->AddColumn(new NanotimeTableColumn(5, "Wait time", 80,
459		20, 1000, false, B_TRUNCATE_END, B_ALIGN_RIGHT));
460
461	fWaitObjectsTree->AddTreeTableListener(this);
462}
463
464
465ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
466ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::~AbstractWaitObjectsPage()
467{
468	fWaitObjectsTree->SetTreeTableModel(NULL);
469	delete fWaitObjectsTreeModel;
470}
471
472
473ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
474void
475ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::SetModel(ModelType* model)
476{
477	if (model == fModel)
478		return;
479
480	if (fModel != NULL) {
481		fWaitObjectsTree->SetTreeTableModel(NULL);
482		delete fWaitObjectsTreeModel;
483		fWaitObjectsTreeModel = NULL;
484	}
485
486	fModel = model;
487
488	if (fModel != NULL)
489		_UpdateTreeModel();
490}
491
492
493ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
494void
495ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::MessageReceived(BMessage* message)
496{
497	switch (message->what) {
498		case MSG_ABSTRACT_WAIT_OBJECTS_GROUP_BY_NAME:
499			fGroupByName = fGroupByNameCheckBox->Value() == B_CONTROL_ON;
500			_UpdateTreeModel();
501			break;
502		default:
503			BGroupView::MessageReceived(message);
504			break;
505	}
506}
507
508
509ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
510void
511ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::AttachedToWindow()
512{
513	fGroupByNameCheckBox->SetTarget(this);
514}
515
516
517ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
518void
519ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::_UpdateTreeModel()
520{
521	if (fModel != NULL) {
522		fWaitObjectsTree->SetTreeTableModel(NULL);
523		delete fWaitObjectsTreeModel;
524		fWaitObjectsTreeModel = NULL;
525	}
526
527	if (fModel != NULL) {
528		try {
529			fWaitObjectsTreeModel = new WaitObjectsTreeModel(fModel,
530				fGroupByName);
531		} catch (std::bad_alloc&) {
532			// TODO: Report error!
533		}
534		fWaitObjectsTree->SetTreeTableModel(fWaitObjectsTreeModel);
535		fWaitObjectsTree->ResizeAllColumnsToPreferred();
536	}
537}
538
539
540#endif	// ABSTRACT_WAIT_OBJECTS_PAGE_H
541