1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include <DebugEventStream.h>
7
8#include <stdlib.h>
9#include <string.h>
10
11#include <DataIO.h>
12
13#include <system_profiler_defs.h>
14
15
16#define INPUT_BUFFER_SIZE	(128 * 1024)
17
18
19BDebugEventInputStream::BDebugEventInputStream()
20	:
21	fStream(NULL),
22	fFlags(0),
23	fEventMask(0),
24	fBuffer(NULL),
25	fBufferCapacity(0),
26	fBufferSize(0),
27	fBufferPosition(0),
28	fStreamPosition(0),
29	fOwnsBuffer(false)
30{
31}
32
33
34BDebugEventInputStream::~BDebugEventInputStream()
35{
36	Unset();
37
38	if (fOwnsBuffer)
39		free(fBuffer);
40}
41
42
43status_t
44BDebugEventInputStream::SetTo(BDataIO* stream)
45{
46	Unset();
47
48	// set the new values
49	if (stream == NULL)
50		return B_BAD_VALUE;
51
52	fStream = stream;
53
54	// allocate a buffer
55	if (fBuffer == NULL) {
56		fBuffer = (uint8*)malloc(INPUT_BUFFER_SIZE);
57		if (fBuffer == NULL) {
58			Unset();
59			return B_NO_MEMORY;
60		}
61
62		fOwnsBuffer = true;
63		fBufferCapacity = INPUT_BUFFER_SIZE;
64		fBufferSize = 0;
65	}
66
67	return _Init();
68}
69
70
71status_t
72BDebugEventInputStream::SetTo(const void* data, size_t size,
73	bool takeOverOwnership)
74{
75	Unset();
76
77	if (data == NULL || size == 0)
78		return B_BAD_VALUE;
79
80	if (fBuffer != NULL) {
81		if (fOwnsBuffer)
82			free(fBuffer);
83		fBuffer = NULL;
84		fBufferCapacity = 0;
85		fBufferSize = 0;
86	}
87
88	fBuffer = (uint8*)data;
89	fBufferCapacity = fBufferSize = size;
90	fOwnsBuffer = takeOverOwnership;
91
92	return _Init();
93}
94
95
96void
97BDebugEventInputStream::Unset()
98{
99	fStream = NULL;
100	fFlags = 0;
101	fEventMask = 0;
102
103	// If we have a buffer that we own and has the right size, we keep it.
104	if (fOwnsBuffer) {
105		if (fBuffer != NULL && fBufferSize != INPUT_BUFFER_SIZE) {
106			free(fBuffer);
107			fBuffer = NULL;
108			fBufferCapacity = 0;
109			fBufferSize = 0;
110		}
111	} else {
112		fBuffer = NULL;
113		fBufferCapacity = 0;
114		fBufferSize = 0;
115	}
116}
117
118
119status_t
120BDebugEventInputStream::Seek(off_t streamOffset)
121{
122	// TODO: Support for streams, at least for BPositionIOs.
123	if (fStream != NULL)
124		return B_UNSUPPORTED;
125
126	if (streamOffset < 0 || streamOffset > (off_t)fBufferCapacity)
127		return B_BUFFER_OVERFLOW;
128
129	fStreamPosition = 0;
130	fBufferPosition = streamOffset;
131	fBufferSize = fBufferCapacity - streamOffset;
132
133	return B_OK;
134}
135
136
137/*!	\brief Returns the next event in the stream.
138
139	At the end of the stream \c 0 is returned and \c *_buffer is set to \c NULL.
140	For events that don't have data associated with them, \c *_buffer will still
141	be non-NULL, even if dereferencing that address is not allowed.
142
143	\param _event Pointer to a pre-allocated location where the event ID shall
144		be stored.
145	\param _cpu Pointer to a pre-allocated location where the CPU index shall
146		be stored.
147	\param _buffer Pointer to a pre-allocated location where the pointer to the
148		event data shall be stored.
149	\param _streamOffset Pointer to a pre-allocated location where the event
150		header's offset relative to the beginning of the stream shall be stored.
151		May be \c NULL.
152	\return A negative error code in case an error occurred while trying to read
153		the info, the size of the data associated with the event otherwise.
154*/
155ssize_t
156BDebugEventInputStream::ReadNextEvent(uint32* _event, uint32* _cpu,
157	const void** _buffer, off_t* _streamOffset)
158{
159	// get the next header
160	status_t error = _GetData(sizeof(system_profiler_event_header));
161	if (error != B_OK) {
162		if (error == B_BAD_DATA && fBufferSize == 0) {
163			*_buffer = NULL;
164			return 0;
165		}
166		return error;
167	}
168
169	system_profiler_event_header header
170		= *(system_profiler_event_header*)(fBuffer + fBufferPosition);
171
172	off_t streamOffset = fStreamPosition + fBufferPosition;
173
174	// skip the header in the buffer
175	fBufferSize -= sizeof(system_profiler_event_header);
176	fBufferPosition += sizeof(system_profiler_event_header);
177
178	// get the data
179	if (header.size > 0) {
180		error = _GetData(header.size);
181		if (error != B_OK)
182			return error;
183	}
184
185	*_event = header.event;
186	*_cpu = header.cpu;
187	*_buffer = fBuffer + fBufferPosition;
188	if (_streamOffset)
189		*_streamOffset = streamOffset;
190
191	// skip the event in the buffer
192	fBufferSize -= header.size;
193	fBufferPosition += header.size;
194
195	return header.size;
196}
197
198
199status_t
200BDebugEventInputStream::_Init()
201{
202	fStreamPosition = 0;
203	fBufferPosition = 0;
204
205	// get the header
206	status_t error = _GetData(sizeof(debug_event_stream_header));
207	if (error != B_OK) {
208		Unset();
209		return error;
210	}
211	const debug_event_stream_header& header
212		= *(const debug_event_stream_header*)(fBuffer + fBufferPosition);
213
214	fBufferPosition += sizeof(debug_event_stream_header);
215	fBufferSize -= sizeof(debug_event_stream_header);
216
217	// check the header
218	if (strncmp(header.signature, B_DEBUG_EVENT_STREAM_SIGNATURE,
219			sizeof(header.signature)) != 0
220		|| header.version != B_DEBUG_EVENT_STREAM_VERSION
221		|| (header.flags & B_DEBUG_EVENT_STREAM_FLAG_HOST_ENDIAN) == 0) {
222		// TODO: Support non-host endianess!
223		Unset();
224		return B_BAD_DATA;
225	}
226
227	fFlags = header.flags;
228	fEventMask = header.event_mask;
229
230	return B_OK;
231}
232
233
234ssize_t
235BDebugEventInputStream::_Read(void* _buffer, size_t size)
236{
237	uint8* buffer = (uint8*)_buffer;
238	size_t totalBytesRead = 0;
239	ssize_t bytesRead = 0;
240
241	while (size > 0 && (bytesRead = fStream->Read(buffer, size)) > 0) {
242		totalBytesRead += bytesRead;
243		buffer += bytesRead;
244		size -= bytesRead;
245	}
246
247	if (bytesRead < 0)
248		return bytesRead;
249
250	return totalBytesRead;
251}
252
253
254status_t
255BDebugEventInputStream::_GetData(size_t size)
256{
257	if (fBufferSize >= size)
258		return B_OK;
259
260	if (size > fBufferCapacity)
261		return B_BUFFER_OVERFLOW;
262
263	// move remaining data to the start of the buffer
264	if (fBufferSize > 0 && fBufferPosition > 0)
265		memmove(fBuffer, fBuffer + fBufferPosition, fBufferSize);
266	fStreamPosition += fBufferPosition;
267	fBufferPosition = 0;
268
269	// read more data
270	if (fStream != NULL) {
271		ssize_t bytesRead = _Read(fBuffer + fBufferSize,
272			fBufferCapacity - fBufferSize);
273		if (bytesRead < 0)
274			return bytesRead;
275
276		fBufferSize += bytesRead;
277	}
278
279	return fBufferSize >= size ? B_OK : B_BAD_DATA;
280}
281
282
283// #pragma mark - BDebugEventOutputStream
284
285
286BDebugEventOutputStream::BDebugEventOutputStream()
287	:
288	fStream(NULL),
289	fFlags(0)
290{
291}
292
293
294BDebugEventOutputStream::~BDebugEventOutputStream()
295{
296	Unset();
297}
298
299
300status_t
301BDebugEventOutputStream::SetTo(BDataIO* stream, uint32 flags, uint32 eventMask)
302{
303	Unset();
304
305	// set the new values
306	if (stream == NULL)
307		return B_BAD_VALUE;
308
309	fStream = stream;
310	fFlags = /*(flags & B_DEBUG_EVENT_STREAM_FLAG_ZIPPED)
311		|*/ B_DEBUG_EVENT_STREAM_FLAG_HOST_ENDIAN;
312		// TODO: Support zipped data!
313
314	// init and write the header
315	debug_event_stream_header header;
316	memset(header.signature, 0, sizeof(header.signature));
317	strlcpy(header.signature, B_DEBUG_EVENT_STREAM_SIGNATURE,
318		sizeof(header.signature));
319	header.version = B_DEBUG_EVENT_STREAM_VERSION;
320	header.flags = fFlags;
321	header.event_mask = eventMask;
322
323	ssize_t written = fStream->Write(&header, sizeof(header));
324	if (written < 0) {
325		Unset();
326		return written;
327	}
328	if ((size_t)written != sizeof(header)) {
329		Unset();
330		return B_FILE_ERROR;
331	}
332
333	return B_OK;
334}
335
336
337void
338BDebugEventOutputStream::Unset()
339{
340	Flush();
341	fStream = NULL;
342	fFlags = 0;
343}
344
345
346status_t
347BDebugEventOutputStream::Write(const void* buffer, size_t size)
348{
349	if (size == 0)
350		return B_OK;
351	if (buffer == NULL)
352		return B_BAD_VALUE;
353
354	ssize_t written = fStream->Write(buffer, size);
355	if (written < 0)
356		return written;
357	if ((size_t)written != size)
358		return B_FILE_ERROR;
359
360	return B_OK;
361}
362
363
364status_t
365BDebugEventOutputStream::Flush()
366{
367	return B_OK;
368}
369