summaryrefslogblamecommitdiffstats
blob: dd19f7f8be76b7fb200075914b476e23554e4f2e (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
   
                                                            











                                                                       


                        





                          






















                                                                                                                                                                                                                                                                                                                 





                                                                                      

                       

 
                                          




                                           

                                            
                        
                             
                          

                                           













                                                                                   
                              

 

                                      

 

                                          
                         
                                

         
                                       
 
                         









                                                                                              
                                          


                                               
                                   
                
                                  

         










                                              

 
                                          

                                                                                                                                               
                                            
                         
                                


                                                               
                                                                                            

         

                                                                                                                       
 
                                          

                                                               

                 
 
                       



                                                 
                             

 

                                            
                         
                                
         
                                                                                                  



                             
                                                                                 












                                                                                  
                                                                       
                              
         
 




                                                                 

 
                                   












                                                                                  
                                                           
                              
         
 









                                                                                  

 






                                                 



                                           


                                     

 






















                                                                                                                            









                                                                    
                                      
                            
                                             







                                                           
                                                            





                                             
                                                              





                                             



























                                                                                             














                                                                                                         










































                                                                                                  


                  

                                                                            
 
/**
 * Copyright (C) ARM Limited 2013-2014. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include "Buffer.h"

#include "Logging.h"
#include "Sender.h"
#include "SessionData.h"

#define mask (mSize - 1)

enum {
	CODE_PEA      = 1,
	CODE_KEYS     = 2,
	CODE_FORMAT   = 3,
	CODE_MAPS     = 4,
	CODE_COMM     = 5,
	CODE_KEYS_OLD = 6,
};

// Summary Frame Messages
enum {
	MESSAGE_SUMMARY = 1,
	MESSAGE_CORE_NAME = 3,
};

// From gator_marshaling.c
#define NEWLINE_CANARY \
	/* Unix */ \
	"1\n" \
	/* Windows */ \
	"2\r\n" \
	/* Mac OS */ \
	"3\r" \
	/* RISC OS */ \
	"4\n\r" \
	/* Add another character so the length isn't 0x0a bytes */ \
	"5"

Buffer::Buffer(const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : mCore(core), mBufType(buftype), mSize(size), mReadPos(0), mWritePos(0), mCommitPos(0), mAvailable(true), mIsDone(false), mBuf(new char[mSize]), mCommitTime(gSessionData->mLiveRate), mReaderSem(readerSem) {
	if ((mSize & mask) != 0) {
		logg->logError(__FILE__, __LINE__, "Buffer size is not a power of 2");
		handleException();
	}
	frame();
}

Buffer::~Buffer() {
	delete [] mBuf;
}

void Buffer::write(Sender *const sender) {
	if (!commitReady()) {
		return;
	}

	// determine the size of two halves
	int length1 = mCommitPos - mReadPos;
	char *buffer1 = mBuf + mReadPos;
	int length2 = 0;
	char *buffer2 = mBuf;
	if (length1 < 0) {
		length1 = mSize - mReadPos;
		length2 = mCommitPos;
	}

	logg->logMessage("Sending data length1: %i length2: %i", length1, length2);

	// start, middle or end
	if (length1 > 0) {
		sender->writeData(buffer1, length1, RESPONSE_APC_DATA);
	}

	// possible wrap around
	if (length2 > 0) {
		sender->writeData(buffer2, length2, RESPONSE_APC_DATA);
	}

	mReadPos = mCommitPos;
}

bool Buffer::commitReady() const {
	return mCommitPos != mReadPos;
}

int Buffer::bytesAvailable() const {
	int filled = mWritePos - mReadPos;
	if (filled < 0) {
		filled += mSize;
	}

	int remaining = mSize - filled;

	if (mAvailable) {
		// Give some extra room; also allows space to insert the overflow error packet
		remaining -= 200;
	} else {
		// Hysteresis, prevents multiple overflow messages
		remaining -= 2000;
	}

	return remaining;
}

bool Buffer::checkSpace(const int bytes) {
	const int remaining = bytesAvailable();

	if (remaining < bytes) {
		mAvailable = false;
	} else {
		mAvailable = true;
	}

	return mAvailable;
}

int Buffer::contiguousSpaceAvailable() const {
	int remaining = bytesAvailable();
	int contiguous = mSize - mWritePos;
	if (remaining < contiguous) {
		return remaining;
	} else {
		return contiguous;
	}
}

void Buffer::commit(const uint64_t time) {
	// post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload
	const int typeLength = gSessionData->mLocalCapture ? 0 : 1;
	int length = mWritePos - mCommitPos;
	if (length < 0) {
		length += mSize;
	}
	length = length - typeLength - sizeof(int32_t);
	for (size_t byte = 0; byte < sizeof(int32_t); byte++) {
		mBuf[(mCommitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF;
	}

	logg->logMessage("Committing data mReadPos: %i mWritePos: %i mCommitPos: %i", mReadPos, mWritePos, mCommitPos);
	mCommitPos = mWritePos;

	if (gSessionData->mLiveRate > 0) {
		while (time > mCommitTime) {
			mCommitTime += gSessionData->mLiveRate;
		}
	}

	if (!mIsDone) {
		frame();
	}

	// send a notification that data is ready
	sem_post(mReaderSem);
}

void Buffer::check(const uint64_t time) {
	int filled = mWritePos - mCommitPos;
	if (filled < 0) {
		filled += mSize;
	}
	if (filled >= ((mSize * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= mCommitTime)) {
		commit(time);
	}
}

void Buffer::packInt(char *const buf, const int size, int &writePos, int32_t x) {
	int packedBytes = 0;
	int more = true;
	while (more) {
		// low order 7 bits of x
		char b = x & 0x7f;
		x >>= 7;

		if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) {
			more = false;
		} else {
			b |= 0x80;
		}

		buf[(writePos + packedBytes) & /*mask*/(size - 1)] = b;
		packedBytes++;
	}

	writePos = (writePos + packedBytes) & /*mask*/(size - 1);
}

void Buffer::packInt(int32_t x) {
	packInt(mBuf, mSize, mWritePos, x);
}

void Buffer::packInt64(int64_t x) {
	int packedBytes = 0;
	int more = true;
	while (more) {
		// low order 7 bits of x
		char b = x & 0x7f;
		x >>= 7;

		if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) {
			more = false;
		} else {
			b |= 0x80;
		}

		mBuf[(mWritePos + packedBytes) & mask] = b;
		packedBytes++;
	}

	mWritePos = (mWritePos + packedBytes) & mask;
}

void Buffer::writeBytes(const void *const data, size_t count) {
	size_t i;
	for (i = 0; i < count; ++i) {
		mBuf[(mWritePos + i) & mask] = static_cast<const char *>(data)[i];
	}

	mWritePos = (mWritePos + i) & mask;
}

void Buffer::writeString(const char *const str) {
	const int len = strlen(str);
	packInt(len);
	writeBytes(str, len);
}

void Buffer::frame() {
	if (!gSessionData->mLocalCapture) {
		packInt(RESPONSE_APC_DATA);
	}
	// Reserve space for the length
	mWritePos += sizeof(int32_t);
	packInt(mBufType);
	packInt(mCore);
}

void Buffer::summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname) {
	packInt(MESSAGE_SUMMARY);
	writeString(NEWLINE_CANARY);
	packInt64(timestamp);
	packInt64(uptime);
	packInt64(monotonicDelta);
	writeString("uname");
	writeString(uname);
	writeString("");
	check(1);
}

void Buffer::coreName(const int core, const int cpuid, const char *const name) {
	if (checkSpace(3 * MAXSIZE_PACK32 + 0x100)) {
		packInt(MESSAGE_CORE_NAME);
		packInt(core);
		packInt(cpuid);
		writeString(name);
	}
	check(1);
}

bool Buffer::eventHeader(const uint64_t curr_time) {
	bool retval = false;
	if (checkSpace(MAXSIZE_PACK32 + MAXSIZE_PACK64)) {
		packInt(0);	// key of zero indicates a timestamp
		packInt64(curr_time);
		retval = true;
	}

	return retval;
}

bool Buffer::eventTid(const int tid) {
	bool retval = false;
	if (checkSpace(2 * MAXSIZE_PACK32)) {
		packInt(1);	// key of 1 indicates a tid
		packInt(tid);
		retval = true;
	}

	return retval;
}

void Buffer::event(const int32_t key, const int32_t value) {
	if (checkSpace(2 * MAXSIZE_PACK32)) {
		packInt(key);
		packInt(value);
	}
}

void Buffer::event64(const int64_t key, const int64_t value) {
	if (checkSpace(2 * MAXSIZE_PACK64)) {
		packInt64(key);
		packInt64(value);
	}
}

void Buffer::pea(const struct perf_event_attr *const pea, int key) {
	if (checkSpace(2 * MAXSIZE_PACK32 + pea->size)) {
		packInt(CODE_PEA);
		writeBytes(pea, pea->size);
		packInt(key);
	} else {
		logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
		handleException();
	}
	// Don't know the real perf time so use 1 as it will work for now
	check(1);
}

void Buffer::keys(const int count, const __u64 *const ids, const int *const keys) {
	if (checkSpace(2 * MAXSIZE_PACK32 + count * (MAXSIZE_PACK32 + MAXSIZE_PACK64))) {
		packInt(CODE_KEYS);
		packInt(count);
		for (int i = 0; i < count; ++i) {
			packInt64(ids[i]);
			packInt(keys[i]);
		}
	} else {
		logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
		handleException();
	}
	check(1);
}

void Buffer::keysOld(const int keyCount, const int *const keys, const int bytes, const char *const buf) {
	if (checkSpace((2 + keyCount) * MAXSIZE_PACK32 + bytes)) {
		packInt(CODE_KEYS_OLD);
		packInt(keyCount);
		for (int i = 0; i < keyCount; ++i) {
			packInt(keys[i]);
		}
		writeBytes(buf, bytes);
	} else {
		logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
		handleException();
	}
	check(1);
}

void Buffer::format(const int length, const char *const format) {
	if (checkSpace(MAXSIZE_PACK32 + length + 1)) {
		packInt(CODE_FORMAT);
		writeBytes(format, length + 1);
	} else {
		logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
		handleException();
	}
	check(1);
}

void Buffer::maps(const int pid, const int tid, const char *const maps) {
	const int mapsLen = strlen(maps) + 1;
	if (checkSpace(3 * MAXSIZE_PACK32 + mapsLen)) {
		packInt(CODE_MAPS);
		packInt(pid);
		packInt(tid);
		writeBytes(maps, mapsLen);
	} else {
		logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
		handleException();
	}
	check(1);
}

void Buffer::comm(const int pid, const int tid, const char *const image, const char *const comm) {
	const int imageLen = strlen(image) + 1;
	const int commLen = strlen(comm) + 1;
	if (checkSpace(3 * MAXSIZE_PACK32 + imageLen + commLen)) {
		packInt(CODE_COMM);
		packInt(pid);
		packInt(tid);
		writeBytes(image, imageLen);
		writeBytes(comm, commLen);
	} else {
		logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
		handleException();
	}
	check(1);
}

void Buffer::setDone() {
	mIsDone = true;
	commit(0);
}

bool Buffer::isDone() const {
	return mIsDone && mReadPos == mCommitPos && mCommitPos == mWritePos;
}