From 9d4d0666fba96370c54e0d704edc85467ac8094f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Aug 2013 00:02:45 +0200 Subject: [PATCH 01/16] drm/gem: completely close gem_open vs. gem_close races The gem flink name holds a reference onto the object itself, and this self-reference would prevent an flink'ed object from every being freed. To break that loop we remove the flink name when the last userspace handle disappears, i.e. when obj->handle_count reaches 0. Now in gem_open we drop the dev->object_name_lock between the flink name lookup and actually adding the handle. This means a concurrent gem_close of the last handle could result in the flink name getting reaped right inbetween, i.e. Thread 1 Thread 2 gem_open gem_close flink -> obj lookup handle_count drops to 0 remove flink name create_handle handle_count++ If someone now flinks this object again, we'll get a new flink name. We can close this race by removing the lock dropping and making the entire lookup+handle_create sequence atomic. Unfortunately to still be able to share the handle_create logic this requires a handle_create_tail function which drops the lock - we can't hold the object_name_lock while calling into a driver's ->gem_open callback. Note that for flink fixing this race isn't really important, since racing gem_open against gem_close is clearly a userspace bug. And no matter how the race ends, we won't leak any references. But with dma-buf where the userspace dma-buf fd itself is refcounted this is a valid sequence and hence we should fix it. Therefore this patch here is just a warm-up exercise (and for consistency between flink buffer sharing and dma-buf buffer sharing with self-imports). Also note that this extension of the critical section in gem_open protected by dev->object_name_lock only works because it's now a mutex: A spinlock would conflict with the potential memory allocation in idr_preload(). This is exercises by igt/gem_flink_race/flink_name. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie [jy0922.shim: fix up fuzz to apply] Signed-off-by: Joonyoung Shim Change-Id: I7fc3ffb1a77b2b5ca7e04a38c26ccd3a73b67f62 --- drivers/gpu/drm/drm_gem.c | 42 +++++++++++++++++++++++++++++++----------- include/drm/drmP.h | 3 +++ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index a3ce359..690a00a 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -305,23 +305,26 @@ drm_gem_handle_delete(struct drm_file *filp, u32 handle) EXPORT_SYMBOL(drm_gem_handle_delete); /** - * Create a handle for this object. This adds a handle reference - * to the object, which includes a regular reference count. Callers - * will likely want to dereference the object afterwards. + * drm_gem_handle_create_tail - internal functions to create a handle + * + * This expects the dev->object_name_lock to be held already and will drop it + * before returning. Used to avoid races in establishing new handles when + * importing an object from either an flink name or a dma-buf. */ int -drm_gem_handle_create(struct drm_file *file_priv, - struct drm_gem_object *obj, - u32 *handlep) +drm_gem_handle_create_tail(struct drm_file *file_priv, + struct drm_gem_object *obj, + u32 *handlep) { struct drm_device *dev = obj->dev; int ret; + WARN_ON(!mutex_is_locked(&dev->object_name_lock)); + /* * Get the user-visible handle using idr. Preload and perform * allocation under our spinlock. */ - mutex_lock(&dev->object_name_lock); idr_preload(GFP_KERNEL); spin_lock(&file_priv->table_lock); @@ -348,6 +351,21 @@ drm_gem_handle_create(struct drm_file *file_priv, return 0; } + +/** + * Create a handle for this object. This adds a handle reference + * to the object, which includes a regular reference count. Callers + * will likely want to dereference the object afterwards. + */ +int +drm_gem_handle_create(struct drm_file *file_priv, + struct drm_gem_object *obj, + u32 *handlep) +{ + mutex_lock(&obj->dev->object_name_lock); + + return drm_gem_handle_create_tail(file_priv, obj, handlep); +} EXPORT_SYMBOL(drm_gem_handle_create); @@ -554,13 +572,15 @@ drm_gem_open_ioctl(struct drm_device *dev, void *data, mutex_lock(&dev->object_name_lock); obj = idr_find(&dev->object_name_idr, (int) args->name); - if (obj) + if (obj) { drm_gem_object_reference(obj); - mutex_unlock(&dev->object_name_lock); - if (!obj) + } else { + mutex_unlock(&dev->object_name_lock); return -ENOENT; + } - ret = drm_gem_handle_create(file_priv, obj, &handle); + /* drm_gem_handle_create_tail unlocks dev->object_name_lock. */ + ret = drm_gem_handle_create_tail(file_priv, obj, &handle); drm_gem_object_unreference_unlocked(obj); if (ret) return ret; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 802b698..e03afd3 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1703,6 +1703,9 @@ drm_gem_object_unreference_unlocked(struct drm_gem_object *obj) } } +int drm_gem_handle_create_tail(struct drm_file *file_priv, + struct drm_gem_object *obj, + u32 *handlep); int drm_gem_handle_create(struct drm_file *file_priv, struct drm_gem_object *obj, u32 *handlep); -- 2.7.4 From 1b9348e3473ef2489847b4b19b7a2d73142076ac Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Aug 2013 00:02:46 +0200 Subject: [PATCH 02/16] drm/prime: proper locking+refcounting for obj->dma_buf link The export dma-buf cache is semantically similar to an flink name. So semantically it makes sense to treat it the same and remove the name (i.e. the dma_buf pointer) and its references when the last gem handle disappears. Again we need to be careful, but double so: Not just could someone race and export with a gem close ioctl (so we need to recheck obj->handle_count again when assigning the new name), but multiple exports can also race against each another. This is prevented by holding the dev->object_name_lock across the entire section which touches obj->dma_buf. With the new scheme we also need to reinstate the obj->dma_buf link at import time (in case the only reference userspace has held in-between was through the dma-buf fd and not through any native gem handle). For simplicity we don't check whether it's a native object but unconditionally set up that link - with the new scheme of removing the obj->dma_buf reference when the last handle disappears we can do that. To make it clear that this is not just for exported buffers anymore als rename it from export_dma_buf to dma_buf. To make sure that now one can race a fd_to_handle or handle_to_fd with gem_close we use the same tricks as in flink of extending the dev->object_name_locking critical section. With this change we finally have a guaranteed 1:1 relationship (at least for native objects) between gem objects and dma-bufs, even accounting for races (which can happen since the dma-buf itself holds a reference while in-flight). This prevent igt/prime_self_import/export-vs-gem_close-race from Oopsing the kernel. There is still a leak though since the per-file priv dma-buf/handle cache handling is racy. That will be fixed in a later patch. v2: Remove the bogus dma_buf_put from the export_and_register_object failure path if we've raced with the handle count dropping to 0. Change-Id: I89173f8802ccc12fbf48f053a7701c114e92692b Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_fops.c | 1 + drivers/gpu/drm/drm_gem.c | 24 ++++++++++++++-- drivers/gpu/drm/drm_prime.c | 70 +++++++++++++++++++++++++++++++++++---------- include/drm/drmP.h | 12 ++++++-- 4 files changed, 87 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index f69cc33..ae2ba85 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -540,6 +540,7 @@ int drm_release(struct inode *inode, struct file *filp) if (dev->driver->postclose) dev->driver->postclose(dev, file_priv); + if (drm_core_check_feature(dev, DRIVER_PRIME)) drm_prime_destroy_file_private(&file_priv->prime); diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 690a00a..15c838f 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -204,9 +204,14 @@ drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp) drm_prime_remove_buf_handle(&filp->prime, obj->import_attach->dmabuf); } - if (obj->export_dma_buf) { + + /* + * Note: obj->dma_buf can't disappear as long as we still hold a + * handle reference in obj->handle_count. + */ + if (obj->dma_buf) { drm_prime_remove_buf_handle(&filp->prime, - obj->export_dma_buf); + obj->dma_buf); } } @@ -240,6 +245,15 @@ static void drm_gem_object_handle_free(struct drm_gem_object *obj) } } +static void drm_gem_object_exported_dma_buf_free(struct drm_gem_object *obj) +{ + /* Unbreak the reference cycle if we have an exported dma_buf. */ + if (obj->dma_buf) { + dma_buf_put(obj->dma_buf); + obj->dma_buf = NULL; + } +} + static void drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) { @@ -253,8 +267,10 @@ drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) */ mutex_lock(&obj->dev->object_name_lock); - if (--obj->handle_count == 0) + if (--obj->handle_count == 0) { drm_gem_object_handle_free(obj); + drm_gem_object_exported_dma_buf_free(obj); + } mutex_unlock(&obj->dev->object_name_lock); drm_gem_object_unreference_unlocked(obj); @@ -643,6 +659,8 @@ drm_gem_release(struct drm_device *dev, struct drm_file *file_private) void drm_gem_object_release(struct drm_gem_object *obj) { + WARN_ON(obj->dma_buf); + if (obj->filp) fput(obj->filp); } diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 3d57601..5e543e9 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -193,11 +193,8 @@ void drm_gem_dmabuf_release(struct dma_buf *dma_buf) { struct drm_gem_object *obj = dma_buf->priv; - if (obj->export_dma_buf == dma_buf) { - /* drop the reference on the export fd holds */ - obj->export_dma_buf = NULL; - drm_gem_object_unreference_unlocked(obj); - } + /* drop the reference on the export fd holds */ + drm_gem_object_unreference_unlocked(obj); } EXPORT_SYMBOL(drm_gem_dmabuf_release); @@ -298,6 +295,37 @@ struct dma_buf *drm_gem_prime_export(struct drm_device *dev, } EXPORT_SYMBOL(drm_gem_prime_export); +static struct dma_buf *export_and_register_object(struct drm_device *dev, + struct drm_gem_object *obj, + uint32_t flags) +{ + struct dma_buf *dmabuf; + + /* prevent races with concurrent gem_close. */ + if (obj->handle_count == 0) { + dmabuf = ERR_PTR(-ENOENT); + return dmabuf; + } + + dmabuf = dev->driver->gem_prime_export(dev, obj, flags); + if (IS_ERR(dmabuf)) { + /* normally the created dma-buf takes ownership of the ref, + * but if that fails then drop the ref + */ + return dmabuf; + } + + /* + * Note that callers do not need to clean up the export cache + * since the check for obj->handle_count guarantees that someone + * will clean it up. + */ + obj->dma_buf = dmabuf; + get_dma_buf(obj->dma_buf); + + return dmabuf; +} + int drm_gem_prime_handle_to_fd(struct drm_device *dev, struct drm_file *file_priv, uint32_t handle, uint32_t flags, int *prime_fd) @@ -313,15 +341,20 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, /* re-export the original imported object */ if (obj->import_attach) { dmabuf = obj->import_attach->dmabuf; + get_dma_buf(dmabuf); goto out_have_obj; } - if (obj->export_dma_buf) { - dmabuf = obj->export_dma_buf; + mutex_lock(&dev->object_name_lock); + if (obj->dma_buf) { + get_dma_buf(obj->dma_buf); + dmabuf = obj->dma_buf; + mutex_unlock(&dev->object_name_lock); goto out_have_obj; } - dmabuf = dev->driver->gem_prime_export(dev, obj, flags); + dmabuf = export_and_register_object(dev, obj, flags); + mutex_unlock(&dev->object_name_lock); if (IS_ERR(dmabuf)) { /* normally the created dma-buf takes ownership of the ref, * but if that fails then drop the ref @@ -329,14 +362,13 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, ret = PTR_ERR(dmabuf); goto out; } - obj->export_dma_buf = dmabuf; mutex_lock(&file_priv->prime.lock); /* if we've exported this buffer the cheat and add it to the import list * so we get the correct handle back */ ret = drm_prime_add_buf_handle(&file_priv->prime, - obj->export_dma_buf, handle); + dmabuf, handle); if (ret) goto fail_put_dmabuf; @@ -349,7 +381,6 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, return 0; out_have_obj: - get_dma_buf(dmabuf); ret = dma_buf_fd(dmabuf, flags); if (ret < 0) { dma_buf_put(dmabuf); @@ -365,8 +396,6 @@ fail_rm_handle: dmabuf); mutex_unlock(&file_priv->prime.lock); fail_put_dmabuf: - /* clear NOT to be checked when releasing dma_buf */ - obj->export_dma_buf = NULL; dma_buf_put(dmabuf); out: drm_gem_object_unreference_unlocked(obj); @@ -448,13 +477,22 @@ int drm_gem_prime_fd_to_handle(struct drm_device *dev, goto out_put; /* never seen this one, need to import */ + mutex_lock(&dev->object_name_lock); obj = dev->driver->gem_prime_import(dev, dma_buf); if (IS_ERR(obj)) { ret = PTR_ERR(obj); - goto out_put; + goto out_unlock; + } + + if (obj->dma_buf) { + WARN_ON(obj->dma_buf != dma_buf); + } else { + obj->dma_buf = dma_buf; + get_dma_buf(dma_buf); } - ret = drm_gem_handle_create(file_priv, obj, handle); + /* drm_gem_handle_create_tail unlocks dev->object_name_lock. */ + ret = drm_gem_handle_create_tail(file_priv, obj, handle); drm_gem_object_unreference_unlocked(obj); if (ret) goto out_put; @@ -475,6 +513,8 @@ fail: * to detach.. which seems ok.. */ drm_gem_handle_delete(file_priv, *handle); +out_unlock: + mutex_lock(&dev->object_name_lock); out_put: dma_buf_put(dma_buf); mutex_unlock(&file_priv->prime.lock); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index e03afd3..ceaa68f 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -690,8 +690,16 @@ struct drm_gem_object { void *driver_private; - /* dma buf exported from this GEM object */ - struct dma_buf *export_dma_buf; + /** + * dma_buf - dma buf associated with this GEM object + * + * Pointer to the dma-buf associated with this gem object (either + * through importing or exporting). We break the resulting reference + * loop when the last gem handle for this object is released. + * + * Protected by obj->object_name_lock + */ + struct dma_buf *dma_buf; /** * import_attach - dma buf attachment backing this object -- 2.7.4 From 92b6cccaf16b10f6075a65f3085d07c882d90dfb Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Aug 2013 00:02:47 +0200 Subject: [PATCH 03/16] drm/prime: Simplify drm_gem_remove_prime_handles with the reworking semantics and locking of the obj->dma_buf pointer this pointer is always set as long as there's still a gem handle around and a dma_buf associated with this gem object. Also, the per file-priv lookup-cache for dma-buf importing is also unified between foreign and native objects. Hence we don't need to special case the clean any more and can simply drop the clause which only runs for foreing objects, i.e. with obj->import_attach set. Note that with this change (actually with the previous one to always set up obj->dma_buf even for foreign objects) it is no longer required to set obj->import_attach when importing a foreing object. So update comments accordingly, too. Change-Id: If153ff3c09c4380b86497d7cb26b5b02b40ec020 Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem.c | 5 ----- include/drm/drmP.h | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 15c838f..34ea609 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -200,11 +200,6 @@ EXPORT_SYMBOL(drm_gem_object_alloc); static void drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp) { - if (obj->import_attach) { - drm_prime_remove_buf_handle(&filp->prime, - obj->import_attach->dmabuf); - } - /* * Note: obj->dma_buf can't disappear as long as we still hold a * handle reference in obj->handle_count. diff --git a/include/drm/drmP.h b/include/drm/drmP.h index ceaa68f..ad073eb 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -710,6 +710,11 @@ struct drm_gem_object { * * The driver's ->gem_free_object callback is responsible for cleaning * up the dma_buf attachment and references acquired at import time. + * + * Note that the drm gem/prime core does not depend upon drivers setting + * this field any more. So for drivers where this doesn't make sense + * (e.g. virtual devices or a displaylink behind an usb bus) they can + * simply leave it as NULL. */ struct dma_buf_attachment *import_attach; }; -- 2.7.4 From fd0692b0b8d1db6cd1b2b22bacc8f3f0ce3920cd Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Aug 2013 00:02:48 +0200 Subject: [PATCH 04/16] drm/prime: make drm_prime_lookup_buf_handle static ... and move it to the top of the function to avoid a forward declaration. Change-Id: I1e8ce7ca0bd845ff10d72da56a6a992f3eca75ac Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_prime.c | 29 +++++++++++++++-------------- include/drm/drmP.h | 1 - 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 5e543e9..ed1ea5c 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -83,6 +83,21 @@ static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv, return 0; } +static int drm_prime_lookup_buf_handle(struct drm_prime_file_private *prime_fpriv, + struct dma_buf *dma_buf, + uint32_t *handle) +{ + struct drm_prime_member *member; + + list_for_each_entry(member, &prime_fpriv->head, entry) { + if (member->dma_buf == dma_buf) { + *handle = member->handle; + return 0; + } + } + return -ENOENT; +} + static int drm_gem_map_attach(struct dma_buf *dma_buf, struct device *target_dev, struct dma_buf_attachment *attach) @@ -655,20 +670,6 @@ void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv) } EXPORT_SYMBOL(drm_prime_destroy_file_private); -int drm_prime_lookup_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle) -{ - struct drm_prime_member *member; - - list_for_each_entry(member, &prime_fpriv->head, entry) { - if (member->dma_buf == dma_buf) { - *handle = member->handle; - return 0; - } - } - return -ENOENT; -} -EXPORT_SYMBOL(drm_prime_lookup_buf_handle); - void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf) { mutex_lock(&prime_fpriv->lock); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index ad073eb..4dde3ed 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1636,7 +1636,6 @@ extern void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *s void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv); void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv); -int drm_prime_lookup_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle); void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf); #if DRM_DEBUG_CODE -- 2.7.4 From 0973810973ea924df02e0090232e54f6294a8e23 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Aug 2013 00:02:49 +0200 Subject: [PATCH 05/16] drm/prime: Always add exported buffers to the handle cache ... not only when the dma-buf is freshly created. In contrived examples someone else could have exported/imported the dma-buf already and handed us the gem object with a flink name. If such on object gets reexported as a dma_buf we won't have it in the handle cache already, which breaks the guarantee that for dma-buf imports we always hand back an existing handle if there is one. This is exercised by igt/prime_self_import/with_one_bo_two_files Now if we extend the locked sections just a notch more we can also plug th racy buf/handle cache setup in handle_to_fd: If evil userspace races a concurrent gem close against a prime export operation we can end up tearing down the gem handle before the dma buf handle cache is set up. When handle_to_fd gets around to adding the handle to the cache there will be no one left to clean it up, effectily leaking the bo (and the dma-buf, since the handle cache holds a ref on the dma-buf): Thread A Thread B handle_to_fd: lookup gem object from handle creates new dma_buf gem_close on the same handle obj->dma_buf is set, but file priv buf handle cache has no entry obj->handle_count drops to 0 drm_prime_add_buf_handle sets up the handle cache -> We have a dma-buf reference in the handle cache, but since the handle_count of the gem object already dropped to 0 no on will clean it up. When closing the drm device fd we'll hit the WARN_ON in drm_prime_destroy_file_private. The important change is to extend the critical section of the filp->prime.lock to cover the gem handle lookup. This serializes with a concurrent gem handle close. This leak is exercised by igt/prime_self_import/export-vs-gem_close-race Change-Id: I19ceb9107a318dc299eb103df4042684f0a4252e Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem.c | 6 ++-- drivers/gpu/drm/drm_prime.c | 81 +++++++++++++++++++++++++++------------------ include/drm/drmP.h | 2 +- 3 files changed, 53 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 34ea609..eb2199b 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -204,10 +204,12 @@ drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp) * Note: obj->dma_buf can't disappear as long as we still hold a * handle reference in obj->handle_count. */ + mutex_lock(&filp->prime.lock); if (obj->dma_buf) { - drm_prime_remove_buf_handle(&filp->prime, - obj->dma_buf); + drm_prime_remove_buf_handle_locked(&filp->prime, + obj->dma_buf); } + mutex_unlock(&filp->prime.lock); } static void drm_gem_object_ref_bug(struct kref *list_kref) diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index ed1ea5c..7ae2bfc 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -83,6 +83,19 @@ static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv, return 0; } +static struct dma_buf *drm_prime_lookup_buf_by_handle(struct drm_prime_file_private *prime_fpriv, + uint32_t handle) +{ + struct drm_prime_member *member; + + list_for_each_entry(member, &prime_fpriv->head, entry) { + if (member->handle == handle) + return member->dma_buf; + } + + return NULL; +} + static int drm_prime_lookup_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle) @@ -146,9 +159,8 @@ static void drm_gem_map_detach(struct dma_buf *dma_buf, attach->priv = NULL; } -static void drm_prime_remove_buf_handle_locked( - struct drm_prime_file_private *prime_fpriv, - struct dma_buf *dma_buf) +void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpriv, + struct dma_buf *dma_buf) { struct drm_prime_member *member, *safe; @@ -337,6 +349,8 @@ static struct dma_buf *export_and_register_object(struct drm_device *dev, */ obj->dma_buf = dmabuf; get_dma_buf(obj->dma_buf); + /* Grab a new ref since the callers is now used by the dma-buf */ + drm_gem_object_reference(obj); return dmabuf; } @@ -349,10 +363,20 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, int ret = 0; struct dma_buf *dmabuf; + mutex_lock(&file_priv->prime.lock); obj = drm_gem_object_lookup(dev, file_priv, handle); - if (!obj) - return -ENOENT; + if (!obj) { + ret = -ENOENT; + goto out_unlock; + } + + dmabuf = drm_prime_lookup_buf_by_handle(&file_priv->prime, handle); + if (dmabuf) { + get_dma_buf(dmabuf); + goto out_have_handle; + } + mutex_lock(&dev->object_name_lock); /* re-export the original imported object */ if (obj->import_attach) { dmabuf = obj->import_attach->dmabuf; @@ -360,45 +384,45 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, goto out_have_obj; } - mutex_lock(&dev->object_name_lock); if (obj->dma_buf) { get_dma_buf(obj->dma_buf); dmabuf = obj->dma_buf; - mutex_unlock(&dev->object_name_lock); goto out_have_obj; } dmabuf = export_and_register_object(dev, obj, flags); - mutex_unlock(&dev->object_name_lock); if (IS_ERR(dmabuf)) { /* normally the created dma-buf takes ownership of the ref, * but if that fails then drop the ref */ ret = PTR_ERR(dmabuf); + mutex_unlock(&dev->object_name_lock); goto out; } - mutex_lock(&file_priv->prime.lock); - /* if we've exported this buffer the cheat and add it to the import list - * so we get the correct handle back +out_have_obj: + /* + * If we've exported this buffer then cheat and add it to the import list + * so we get the correct handle back. We must do this under the + * protection of dev->object_name_lock to ensure that a racing gem close + * ioctl doesn't miss to remove this buffer handle from the cache. */ ret = drm_prime_add_buf_handle(&file_priv->prime, dmabuf, handle); + mutex_unlock(&dev->object_name_lock); if (ret) goto fail_put_dmabuf; +out_have_handle: ret = dma_buf_fd(dmabuf, flags); - if (ret < 0) - goto fail_rm_handle; - - *prime_fd = ret; - mutex_unlock(&file_priv->prime.lock); - return 0; - -out_have_obj: - ret = dma_buf_fd(dmabuf, flags); + /* + * We must _not_ remove the buffer from the handle cache since the newly + * created dma buf is already linked in the global obj->dma_buf pointer, + * and that is invariant as long as a userspace gem handle exists. + * Closing the handle will clean out the cache anyway, so we don't leak. + */ if (ret < 0) { - dma_buf_put(dmabuf); + goto fail_put_dmabuf; } else { *prime_fd = ret; ret = 0; @@ -406,14 +430,13 @@ out_have_obj: goto out; -fail_rm_handle: - drm_prime_remove_buf_handle_locked(&file_priv->prime, - dmabuf); - mutex_unlock(&file_priv->prime.lock); fail_put_dmabuf: dma_buf_put(dmabuf); out: drm_gem_object_unreference_unlocked(obj); +out_unlock: + mutex_unlock(&file_priv->prime.lock); + return ret; } EXPORT_SYMBOL(drm_gem_prime_handle_to_fd); @@ -669,11 +692,3 @@ void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv) WARN_ON(!list_empty(&prime_fpriv->head)); } EXPORT_SYMBOL(drm_prime_destroy_file_private); - -void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf) -{ - mutex_lock(&prime_fpriv->lock); - drm_prime_remove_buf_handle_locked(prime_fpriv, dma_buf); - mutex_unlock(&prime_fpriv->lock); -} -EXPORT_SYMBOL(drm_prime_remove_buf_handle); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 4dde3ed..21522da 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1636,7 +1636,7 @@ extern void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *s void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv); void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv); -void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf); +void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf); #if DRM_DEBUG_CODE extern int drm_vma_info(struct seq_file *m, void *data); -- 2.7.4 From 577e2b2137670943a3b5ddd02022173b6efa4a8d Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Mon, 11 Jan 2016 11:57:17 +0900 Subject: [PATCH 06/16] drm/sprd: fix build errors This fixes build errors made by backporting patchse to solve dma-buf issue. Change-Id: I855c8a98a8b6d1ea9119a51c5249ee470052d076 Signed-off-by: Joonyoung Shim --- drivers/gpu/drm/sprd/sprd_drm_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/sprd/sprd_drm_drv.c b/drivers/gpu/drm/sprd/sprd_drm_drv.c index 5c0c9bd..b51851f 100644 --- a/drivers/gpu/drm/sprd/sprd_drm_drv.c +++ b/drivers/gpu/drm/sprd/sprd_drm_drv.c @@ -72,11 +72,11 @@ static int sprd_drm_gem_one_info(int id, void *ptr, void *data) file_priv->tgid, id, atomic_read(&obj->refcount.refcount) - 1, - atomic_read(&obj->handle_count), + obj->handle_count, sprd_gem->size, sprd_gem->flags, buf->pfnmap, - obj->export_dma_buf ? 1 : 0, + obj->dma_buf ? 1 : 0, obj->import_attach ? 1 : 0, obj, obj->name); -- 2.7.4 From 340f63c89f45fed1acabf70fe00198e91b4ae276 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Mon, 11 Jan 2016 12:08:13 +0900 Subject: [PATCH 07/16] drm/sprd: fix locking usage This fixes locking usage made by backporting patchse to solve dma-buf issue. Change-Id: I99c3792cecc5e9974b1fb7c321d8c963de411ea4 Signed-off-by: Joonyoung Shim --- drivers/gpu/drm/sprd/sprd_drm_gem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/sprd/sprd_drm_gem.c b/drivers/gpu/drm/sprd/sprd_drm_gem.c index e33b909..a2a3c9c 100755 --- a/drivers/gpu/drm/sprd/sprd_drm_gem.c +++ b/drivers/gpu/drm/sprd/sprd_drm_gem.c @@ -767,9 +767,9 @@ void *sprd_drm_gem_get_obj_addr(unsigned int name, unsigned int index) struct sprd_drm_gem_buf *buf; int domain_num = 0; - spin_lock(&sprd_drm_dev->object_name_lock); + mutex_lock(&sprd_drm_dev->object_name_lock); obj = idr_find(&sprd_drm_dev->object_name_idr, (int) name); - spin_unlock(&sprd_drm_dev->object_name_lock); + mutex_unlock(&sprd_drm_dev->object_name_lock); if (!obj) { DRM_ERROR("name[%d]failed to lookup gem object.\n", name); -- 2.7.4 From b45b5db804fe82df4d94cafbb9537aff61139ad6 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Tue, 22 Dec 2015 09:44:51 +0530 Subject: [PATCH 08/16] staging/ion: Add support to get ion handle from dma buf Currently we can only import dma buf fd's to get ion_handle. Adding support to import dma buf handles to support kernel use cases. Change-Id: I85b6027b6b142e3f91bce51b717e408530d5523c Signed-off-by: Rohit kumar --- drivers/staging/android/ion/ion.c | 26 ++++++++++++++++---------- include/linux/ion.h | 16 +++++++++++++--- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index c8b4e4d..54337be 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -1302,24 +1302,16 @@ int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle) } EXPORT_SYMBOL(ion_share_dma_buf_fd); -struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) +struct ion_handle *get_ion_handle_from_dmabuf(struct ion_client *client, struct dma_buf *dmabuf) { - struct dma_buf *dmabuf; struct ion_buffer *buffer; struct ion_handle *handle; int ret; - dmabuf = dma_buf_get(fd); - if (IS_ERR(dmabuf)) { - pr_err("ion_import_dma_buf() dmabuf=0x%lx, fd:%d, dma_buf_get error!\n", - (unsigned long)dmabuf, fd); - return ERR_PTR(PTR_ERR(dmabuf)); - } /* if this memory came from ion */ - if (dmabuf->ops != &dma_buf_ops) { pr_err("%s: can not import dmabuf from another exporter\n", - __func__); + __func__); dma_buf_put(dmabuf); return ERR_PTR(-EINVAL); } @@ -1351,6 +1343,20 @@ end: dma_buf_put(dmabuf); return handle; } +EXPORT_SYMBOL(get_ion_handle_from_dmabuf); + +struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) +{ + struct dma_buf *dmabuf; + + dmabuf = dma_buf_get(fd); + if (IS_ERR(dmabuf)) { + pr_err("ion_import_dma_buf() dmabuf=0x%lx, fd:%d, dma_buf_get error!\n", + (unsigned long)dmabuf, fd); + return ERR_PTR(PTR_ERR(dmabuf)); + } + return get_ion_handle_from_dmabuf(client, dmabuf); +} EXPORT_SYMBOL(ion_import_dma_buf); static int ion_invalidate_for_cpu(struct ion_client *client, int fd) diff --git a/include/linux/ion.h b/include/linux/ion.h index 7081a34..0d622aa 100644 --- a/include/linux/ion.h +++ b/include/linux/ion.h @@ -212,13 +212,23 @@ struct dma_buf *ion_share_dma_buf(struct ion_client *client, int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle); /** + * get_ion_handle_from_dmabuf() - given an dma-buf from the ion exporter get handle + * @client: the client + * @dma_buf: the dma-buf + * + * Given an dma-buf that was allocated through ion via ion_share_dma_buf, + * import that dma-buf and return a handle representing it. If a dma-buf from + * another exporter is passed in this function will return ERR_PTR(-EINVAL) + */ +struct ion_handle *get_ion_handle_from_dmabuf(struct ion_client *client, struct dma_buf *dma_buf); + +/** * ion_import_dma_buf() - given an dma-buf fd from the ion exporter get handle * @client: the client * @fd: the dma-buf fd * - * Given an dma-buf fd that was allocated through ion via ion_share_dma_buf, - * import that fd and return a handle representing it. If a dma-buf from - * another exporter is passed in this function will return ERR_PTR(-EINVAL) + * Given an dma-buf fd that was allocated through ion via ion_share_dma_buf_fd, + * import that fd and return a handle representing it. */ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd); -- 2.7.4 From 3dda3cb0ccaf0d66a34a2be31e8f142152755397 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Mon, 11 Jan 2016 15:20:13 +0530 Subject: [PATCH 09/16] Tizen: ion: Fix dma_buf refcount issue in get_ion_handle_from_dmabuf Change-Id: If9eef4cc5dcdf89e89af062a56607b01e3640c25 Signed-off-by: Rohit kumar --- drivers/staging/android/ion/ion.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 54337be..fea70ba 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -1312,7 +1312,6 @@ struct ion_handle *get_ion_handle_from_dmabuf(struct ion_client *client, struct if (dmabuf->ops != &dma_buf_ops) { pr_err("%s: can not import dmabuf from another exporter\n", __func__); - dma_buf_put(dmabuf); return ERR_PTR(-EINVAL); } buffer = dmabuf->priv; @@ -1340,7 +1339,6 @@ struct ion_handle *get_ion_handle_from_dmabuf(struct ion_client *client, struct } end: - dma_buf_put(dmabuf); return handle; } EXPORT_SYMBOL(get_ion_handle_from_dmabuf); @@ -1348,6 +1346,7 @@ EXPORT_SYMBOL(get_ion_handle_from_dmabuf); struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) { struct dma_buf *dmabuf; + struct ion_handle *handle; dmabuf = dma_buf_get(fd); if (IS_ERR(dmabuf)) { @@ -1355,7 +1354,9 @@ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) (unsigned long)dmabuf, fd); return ERR_PTR(PTR_ERR(dmabuf)); } - return get_ion_handle_from_dmabuf(client, dmabuf); + handle = get_ion_handle_from_dmabuf(client, dmabuf); + dma_buf_put(dmabuf); + return handle; } EXPORT_SYMBOL(ion_import_dma_buf); -- 2.7.4 From bb9aede94a482244860963844aaf4acec32e924e Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Thu, 10 Dec 2015 19:10:52 +0900 Subject: [PATCH 10/16] drm/sprd: use prime dma-buf to convert fd and gem handle The sprd drm driver used specific functions to convert fd and gem handle but they don't support some mechanism of prime dma-buf functions like reusing of handle exported already, so use prime dma-buf functions. Change-Id: Ieb591944015bfab0cb15cc21d714f70bffe4b18c Signed-off-by: Joonyoung Shim --- drivers/gpu/drm/sprd/sprd_drm_drv.c | 7 ++-- drivers/gpu/drm/sprd/sprd_drm_gem.c | 64 +++++++++++-------------------------- drivers/gpu/drm/sprd/sprd_drm_gem.h | 10 +++--- 3 files changed, 28 insertions(+), 53 deletions(-) diff --git a/drivers/gpu/drm/sprd/sprd_drm_drv.c b/drivers/gpu/drm/sprd/sprd_drm_drv.c index b51851f..4f75743 100644 --- a/drivers/gpu/drm/sprd/sprd_drm_drv.c +++ b/drivers/gpu/drm/sprd/sprd_drm_drv.c @@ -638,11 +638,14 @@ static struct drm_driver sprd_drm_driver = { .dumb_create = sprd_drm_gem_dumb_create, .dumb_map_offset = sprd_drm_gem_dumb_map_offset, .dumb_destroy = sprd_drm_gem_dumb_destroy, - .prime_handle_to_fd = sprd_drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = sprd_drm_gem_prime_fd_to_handle, + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, #ifdef CONFIG_DRM_SPRD_DMABUF .gem_prime_export = sprd_dmabuf_prime_export, .gem_prime_import = sprd_dmabuf_prime_import, +#else + .gem_prime_export = sprd_prime_export, + .gem_prime_import = sprd_prime_import, #endif .ioctls = sprd_ioctls, .fops = &sprd_drm_driver_fops, diff --git a/drivers/gpu/drm/sprd/sprd_drm_gem.c b/drivers/gpu/drm/sprd/sprd_drm_gem.c index a2a3c9c..cb555b2 100755 --- a/drivers/gpu/drm/sprd/sprd_drm_gem.c +++ b/drivers/gpu/drm/sprd/sprd_drm_gem.c @@ -485,61 +485,42 @@ int sprd_drm_gem_create_index_ioctl(struct drm_device *dev, void *data, return 0; } -int sprd_drm_gem_prime_handle_to_fd(struct drm_device *dev, - struct drm_file *file_priv, uint32_t handle, - uint32_t flags, int *prime_fd) +struct dma_buf *sprd_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, int flags) { - int ret = 0; - struct sprd_drm_gem_obj *sprd_gem_obj; - struct drm_gem_object *obj; - struct sprd_drm_gem_buf *buf; - struct sprd_drm_private *private; - - if (!handle) { - DRM_ERROR("%s: Handle to fd failed. Null handle\n", __func__); - return -EINVAL; - } - - obj = drm_gem_object_lookup(dev, file_priv, handle); - if (!obj) { - DRM_ERROR("failed to lookup gem object.\n"); - return -EINVAL; - } - - private = dev->dev_private; - sprd_gem_obj = to_sprd_gem_obj(obj); - buf = sprd_gem_obj->buffer; - *prime_fd = ion_share_dma_buf_fd(private->sprd_drm_ion_client, - buf->ion_handle); - drm_gem_object_unreference(obj); + struct sprd_drm_private *private = dev->dev_private; + struct sprd_drm_gem_obj *sprd_gem_obj = to_sprd_gem_obj(obj); + struct sprd_drm_gem_buf *buf = sprd_gem_obj->buffer; + struct dma_buf *dmabuf; - if (*prime_fd == -EINVAL) { - prime_fd = NULL; - return -EINVAL; - } + dmabuf = ion_share_dma_buf(private->sprd_drm_ion_client, + buf->ion_handle); + if (IS_ERR(dmabuf)) + pr_err("%s: dmabuf is error and dmabuf is %p!\n", + __func__, dmabuf); - return ret; + return dmabuf; } -int sprd_drm_gem_prime_fd_to_handle(struct drm_device *dev, - struct drm_file *file_priv, int prime_fd, uint32_t *handle) +struct drm_gem_object *sprd_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf) { struct ion_handle *ion_handle; struct sprd_drm_gem_obj *sprd_gem_obj; unsigned long size; struct sprd_drm_gem_buf *buf = NULL; unsigned int i = 0, nr_pages = 0, heap_id; - int ret = 0, gem_handle; + int ret = 0; struct sprd_drm_private *private; struct scatterlist *sg = NULL; struct drm_gem_object *obj; unsigned long sgt_size; private = dev->dev_private; - ion_handle = ion_import_dma_buf(private->sprd_drm_ion_client, prime_fd); + ion_handle = get_ion_handle_from_dmabuf(private->sprd_drm_ion_client, dma_buf); if (IS_ERR_OR_NULL(ion_handle)) { DRM_ERROR("Unable to import dmabuf\n"); - return -EINVAL; + return ERR_PTR(-EINVAL); } ion_handle_get_size(private->sprd_drm_ion_client, @@ -629,14 +610,7 @@ int sprd_drm_gem_prime_fd_to_handle(struct drm_device *dev, DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n", (unsigned long)buf->dma_addr, buf->size); - ret = sprd_drm_gem_handle_create(&sprd_gem_obj->base, file_priv, - &gem_handle); - if (ret) { - sprd_drm_gem_destroy(sprd_gem_obj); - return ret; - } - *handle = gem_handle; - return 0; + return obj; err_buf: buf->dma_addr = (dma_addr_t)NULL; @@ -652,7 +626,7 @@ err_fini_buf: err: ion_free(private->sprd_drm_ion_client, ion_handle); - return ret; + return ERR_PTR(ret); } void *sprd_drm_gem_get_dma_addr(struct drm_device *dev, diff --git a/drivers/gpu/drm/sprd/sprd_drm_gem.h b/drivers/gpu/drm/sprd/sprd_drm_gem.h index 7c1a22a..750bdf0 100644 --- a/drivers/gpu/drm/sprd/sprd_drm_gem.h +++ b/drivers/gpu/drm/sprd/sprd_drm_gem.h @@ -117,13 +117,11 @@ int sprd_drm_gem_create_ioctl(struct drm_device *dev, void *data, int sprd_drm_gem_create_index_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -int sprd_drm_gem_prime_handle_to_fd(struct drm_device *dev, - struct drm_file *file_priv, uint32_t handle, uint32_t flags, - int *prime_fd); - -int sprd_drm_gem_prime_fd_to_handle(struct drm_device *dev, - struct drm_file *file_priv, int prime_fd, uint32_t *handle); +struct dma_buf *sprd_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, int flags); +struct drm_gem_object *sprd_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf); /* * get dma address from gem handle and this function could be used for -- 2.7.4 From 2530fee57c406e59704d9e35b1cf326e20cdab1f Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Tue, 12 Jan 2016 15:02:31 +0530 Subject: [PATCH 11/16] Revert "TizenYoung23gdtv: drm: add ion.h." This reverts commit 4748300326836a2f632b60f8ec8d8cf01f2b4f85. --- include/drm/drmP.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 21522da..694601d 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -76,7 +76,6 @@ #include #include -#include #define __OS_HAS_AGP (defined(CONFIG_AGP) || (defined(CONFIG_AGP_MODULE) && defined(MODULE))) #define __OS_HAS_MTRR (defined(CONFIG_MTRR)) -- 2.7.4 From ffc22173a9ce1cdde55ba18769423f36bc56aec2 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Tue, 12 Jan 2016 15:04:07 +0530 Subject: [PATCH 12/16] staging/ion: sync ion.h with include/linux/ion.h Change-Id: I7961603a6f9615cf7768841edf0168461b477007 Signed-off-by: Rohit kumar --- drivers/staging/android/ion/ion.h | 55 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/drivers/staging/android/ion/ion.h b/drivers/staging/android/ion/ion.h index dcd2a0c..9559b4f 100644 --- a/drivers/staging/android/ion/ion.h +++ b/drivers/staging/android/ion/ion.h @@ -148,6 +148,28 @@ int ion_phys(struct ion_client *client, struct ion_handle *handle, ion_phys_addr_t *addr, size_t *len); /** + * ion_is_phys - returns 0 if CONTIG Heap else -1 + * @client: the client + * @handle: the handle + * + * This function queries the heap for a particular handle to check if + * it is contiguous heap by checking phys() implementation. + * Returns -EINVAL if the handle is invalid. + */ +int ion_is_phys(struct ion_client *client, struct ion_handle *handle); + +/** + * ion_is_cached - returns 0 if CACHED Heap else -1 + * @client: the client + * @handle: the handle + * + * This function queries the heap for a particular handle to check if + * it is cached heap or not. + * Returns -EINVAL if the handle is invalid. + */ +int ion_is_cached(struct ion_client *client, struct ion_handle *handle); + +/** * ion_map_dma - return an sg_table describing a handle * @client: the client * @handle: the handle @@ -191,14 +213,41 @@ struct dma_buf *ion_share_dma_buf(struct ion_client *client, int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle); /** + * get_ion_handle_from_dmabuf() - given an dma-buf from the ion exporter get handle + * @client: the client + * @dma_buf: the dma-buf + * + * Given an dma-buf that was allocated through ion via ion_share_dma_buf, + * import that dma-buf and return a handle representing it. If a dma-buf from + * another exporter is passed in this function will return ERR_PTR(-EINVAL) + */ +struct ion_handle *get_ion_handle_from_dmabuf(struct ion_client *client, struct dma_buf *dma_buf); + +/** * ion_import_dma_buf() - given an dma-buf fd from the ion exporter get handle * @client: the client * @fd: the dma-buf fd * - * Given an dma-buf fd that was allocated through ion via ion_share_dma_buf, - * import that fd and return a handle representing it. If a dma-buf from - * another exporter is passed in this function will return ERR_PTR(-EINVAL) + * Given an dma-buf fd that was allocated through ion via ion_share_dma_buf_fd, + * import that fd and return a handle representing it. */ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd); +/** + * ion_handle_get_size - get the allocated size of a given handle + * + * @client - client who allocated the handle + * @handle - handle to get the size + * @size - pointer to store the size + * + * gives the allocated size of a handle. returns 0 on success, negative + * value on error + * + * NOTE: This is intended to be used only to get a size to pass to map_iommu. + * You should *NOT* rely on this for any other usage. + */ + +int ion_handle_get_size(struct ion_client *client, struct ion_handle *handle, + unsigned long *size, unsigned int *heap_id); + #endif /* _LINUX_ION_H */ -- 2.7.4 From 4be487b3c2e3c9736770ea23ef7db5fe4e979732 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Wed, 6 Jan 2016 14:54:11 +0530 Subject: [PATCH 13/16] drm/sprd: include correct ion.h header file in sprd_drm This patch updates sprd_drm to include staging/ion header file instead of include/linux/ion.h as we are now using staging ion driver. Change-Id: I799c8553aa43278ecab85bc55d518b572b5ef9ea Signed-off-by: Rohit kumar --- drivers/gpu/drm/sprd/Makefile | 2 +- drivers/gpu/drm/sprd/sprd_drm_drv.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile index bc7846d..e85c0be 100644 --- a/drivers/gpu/drm/sprd/Makefile +++ b/drivers/gpu/drm/sprd/Makefile @@ -3,7 +3,7 @@ # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. ccflags-y := -Iinclude/uapi/drm -Idrivers/gpu/drm/sprd -Iinclude/drm -ccflags-y += -Idrivers/video/sprdfb +ccflags-y += -Idrivers/video/sprdfb -Idrivers/staging/android/ion sprddrm-y := sprd_drm_drv.o sprd_drm_buf.o sprd_drm_gem.o \ sprd_drm_core.o sprd_drm_irq.o diff --git a/drivers/gpu/drm/sprd/sprd_drm_drv.h b/drivers/gpu/drm/sprd/sprd_drm_drv.h index 326da87..dcd0fa0 100644 --- a/drivers/gpu/drm/sprd/sprd_drm_drv.h +++ b/drivers/gpu/drm/sprd/sprd_drm_drv.h @@ -16,7 +16,7 @@ #define _SPRD_DRM_DRV_H_ #include -#include +#include "ion.h" #include "drm.h" #include -- 2.7.4 From 03a9b03a2ab2af40838a105c6fdfdd6091bc8ae1 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Tue, 12 Jan 2016 11:03:42 +0900 Subject: [PATCH 14/16] staging/ion: decrease gem reference count in release of dma-buf Gem reference count is increased hen dma-buf is exported, so release of dma-buf should decrease gem reference count. Change-Id: Id298ea79aa14908860e5d87527813994dfdb790d Signed-off-by: Joonyoung Shim Signed-off-by: Rohit kumar --- drivers/gpu/drm/sprd/sprd_drm_buf.c | 4 ++-- drivers/gpu/drm/sprd/sprd_drm_gem.c | 2 ++ drivers/gpu/drm/sprd/sprd_drm_gem.h | 2 ++ drivers/staging/android/ion/ion.c | 25 +++++++++++++++++++++++++ drivers/staging/android/ion/ion.h | 7 ++++++- drivers/staging/android/ion/ion_priv.h | 3 +++ 6 files changed, 40 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/sprd/sprd_drm_buf.c b/drivers/gpu/drm/sprd/sprd_drm_buf.c index b7ad00b..f1b35c8 100644 --- a/drivers/gpu/drm/sprd/sprd_drm_buf.c +++ b/drivers/gpu/drm/sprd/sprd_drm_buf.c @@ -67,8 +67,8 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, if (IS_CACHABLE_BUFFER(flags)) mem_flags = ION_FLAG_CACHED; - buf->ion_handle = ion_alloc(private->sprd_drm_ion_client, buf->size, - SZ_4K, heap_id_mask, mem_flags); + buf->ion_handle = ion_alloc_with_gem(private->sprd_drm_ion_client, + buf->size, SZ_4K, heap_id_mask, mem_flags, buf->obj); if (IS_ERR((void *)buf->ion_handle)) { DRM_ERROR("%s Could not allocate\n", __func__); diff --git a/drivers/gpu/drm/sprd/sprd_drm_gem.c b/drivers/gpu/drm/sprd/sprd_drm_gem.c index cb555b2..fc8b833 100755 --- a/drivers/gpu/drm/sprd/sprd_drm_gem.c +++ b/drivers/gpu/drm/sprd/sprd_drm_gem.c @@ -376,6 +376,8 @@ struct sprd_drm_gem_obj *sprd_drm_gem_create(struct drm_device *dev, /* set memory type and cache attribute from user side. */ sprd_gem_obj->flags = args->flags; + buf->obj = &sprd_gem_obj->base; + ret = sprd_drm_alloc_buf(dev, buf, args->flags); if (ret < 0) goto err_gem_fini; diff --git a/drivers/gpu/drm/sprd/sprd_drm_gem.h b/drivers/gpu/drm/sprd/sprd_drm_gem.h index 750bdf0..9e0de42 100644 --- a/drivers/gpu/drm/sprd/sprd_drm_gem.h +++ b/drivers/gpu/drm/sprd/sprd_drm_gem.h @@ -55,6 +55,8 @@ struct sprd_drm_gem_buf { bool pfnmap; unsigned int bufcount; dma_addr_t idx_addr[SPRD_DRM_GEM_MAX_INDEX_ADDR]; + + struct drm_gem_object *obj; }; struct drm_sprd_gem_object_wait_list_entry { diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index fea70ba..7258486 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -36,6 +36,9 @@ #include #include #include +#ifdef CONFIG_DRM_SPRD +#include +#endif #include "ion.h" #include "ion_priv.h" @@ -558,6 +561,23 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, } EXPORT_SYMBOL(ion_alloc); +#ifdef CONFIG_DRM_SPRD +struct ion_handle *ion_alloc_with_gem(struct ion_client *client, size_t len, + size_t align, unsigned int heap_id_mask, + unsigned int flags, + struct drm_gem_object *obj) +{ + struct ion_handle *handle; + + handle = ion_alloc(client, len, align, heap_id_mask, flags); + if (!IS_ERR(handle)) + handle->buffer->obj = obj; + + return handle; +} +EXPORT_SYMBOL(ion_alloc_with_gem); +#endif + void ion_free(struct ion_client *client, struct ion_handle *handle) { bool valid_handle; @@ -1194,6 +1214,11 @@ static void ion_dma_buf_release(struct dma_buf *dmabuf) { struct ion_buffer *buffer = dmabuf->priv; ion_buffer_put(buffer); + +#ifdef CONFIG_DRM_SPRD + if (buffer->obj) + drm_gem_object_unreference_unlocked(buffer->obj); +#endif } static void *ion_dma_buf_kmap(struct dma_buf *dmabuf, unsigned long offset) diff --git a/drivers/staging/android/ion/ion.h b/drivers/staging/android/ion/ion.h index 9559b4f..0158a66 100644 --- a/drivers/staging/android/ion/ion.h +++ b/drivers/staging/android/ion/ion.h @@ -119,6 +119,12 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, size_t align, unsigned int heap_id_mask, unsigned int flags); +#ifdef CONFIG_DRM_SPRD +struct ion_handle *ion_alloc_with_gem(struct ion_client *client, size_t len, + size_t align, unsigned int heap_id_mask, + unsigned int flags, + struct drm_gem_object *obj); +#endif /** * ion_free - free a handle * @client: the client @@ -249,5 +255,4 @@ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd); int ion_handle_get_size(struct ion_client *client, struct ion_handle *handle, unsigned long *size, unsigned int *heap_id); - #endif /* _LINUX_ION_H */ diff --git a/drivers/staging/android/ion/ion_priv.h b/drivers/staging/android/ion/ion_priv.h index aa0e0b2..4b8745f 100644 --- a/drivers/staging/android/ion/ion_priv.h +++ b/drivers/staging/android/ion/ion_priv.h @@ -89,6 +89,9 @@ struct ion_buffer { pid_t pid; pid_t tid; struct timeval alloc_time; +#ifdef CONFIG_DRM_SPRD + struct drm_gem_object *obj; +#endif }; void ion_buffer_destroy(struct ion_buffer *buffer); -- 2.7.4 From 4015326368d2f373aa0cea002aca985263c7855e Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Wed, 13 Jan 2016 09:44:54 +0900 Subject: [PATCH 15/16] staging/ion: fix build warnings MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This fixed below warnings. include/video/ion_sprd.h:128:7: warning: ‘struct ion_handle’ declared inside parameter list include/video/ion_sprd.h:128:7: warning: its scope is only this definition or declaration, which is probably not what you want include/video/ion_sprd.h:129:29: warning: ‘struct ion_handle’ declared inside parameter list drivers/staging/android/ion/ion.h:126:13: warning: ‘struct drm_gem_object’ declared inside parameter list drivers/staging/android/ion/ion.h:126:13: warning: its scope is only this definition or declaration, which is probably not what you want Change-Id: If51462fad12a1f7d20777835b39b1a276149cef1 Signed-off-by: Joonyoung Shim --- drivers/staging/android/ion/ion.h | 3 +++ include/video/ion_sprd.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/staging/android/ion/ion.h b/drivers/staging/android/ion/ion.h index 0158a66..6be45c2 100644 --- a/drivers/staging/android/ion/ion.h +++ b/drivers/staging/android/ion/ion.h @@ -18,6 +18,9 @@ #define _LINUX_ION_H #include +#ifdef CONFIG_DRM_SPRD +#include +#endif #include "../uapi/ion.h" diff --git a/include/video/ion_sprd.h b/include/video/ion_sprd.h index f099eae..85116a5 100644 --- a/include/video/ion_sprd.h +++ b/include/video/ion_sprd.h @@ -36,6 +36,8 @@ #define ION_DRIVER_VERSION 1 +struct ion_handle; + enum ION_MASTER_ID { ION_GSP = 0, ION_MM, -- 2.7.4 From 6221b2afd5f34d3d7d9dfd9499918fa2bde83814 Mon Sep 17 00:00:00 2001 From: Hyeongsik Min Date: Fri, 15 Jan 2016 09:51:03 +0900 Subject: [PATCH 16/16] gator: Merge gator version 5.23.1 Updated gator driver/daemon version from 5.20 to 5.23.1 Newer version supports ttrace annotation Change-Id: If863ab4ccfd429cb8735635ed812823da8915f8d Signed-off-by: Hyeongsik Min --- drivers/gator/{LICENSE => COPYING} | 0 drivers/gator/Kconfig | 12 +- drivers/gator/Makefile | 39 +- drivers/gator/gator.h | 61 +- drivers/gator/gator_annotate.c | 2 +- drivers/gator/gator_annotate_kernel.c | 2 +- drivers/gator/gator_backtrace.c | 5 +- drivers/gator/gator_buffer.c | 2 +- drivers/gator/gator_buffer_write.c | 2 +- drivers/gator/gator_cookies.c | 104 +- drivers/gator/gator_events_armv6.c | 234 - drivers/gator/gator_events_armv7.c | 314 - drivers/gator/gator_events_block.c | 7 +- drivers/gator/gator_events_irq.c | 7 +- drivers/gator/gator_events_l2c-310.c | 3 +- drivers/gator/gator_events_mali_4xx.c | 42 +- drivers/gator/gator_events_mali_4xx.h | 2 +- drivers/gator/gator_events_mali_common.c | 14 +- drivers/gator/gator_events_mali_common.h | 2 +- drivers/gator/gator_events_mali_midgard.c | 25 +- drivers/gator/gator_events_mali_midgard_hw.c | 146 +- drivers/gator/gator_events_mali_midgard_hw_test.c | 45 +- drivers/gator/gator_events_meminfo.c | 84 +- drivers/gator/gator_events_mmapped.c | 3 +- drivers/gator/gator_events_net.c | 15 +- drivers/gator/gator_events_perf_pmu.c | 218 +- drivers/gator/gator_events_sched.c | 9 +- drivers/gator/gator_events_scorpion.c | 674 - drivers/gator/gator_fs.c | 24 +- drivers/gator/gator_hrtimer_gator.c | 10 +- drivers/gator/gator_iks.c | 8 +- drivers/gator/gator_main.c | 241 +- drivers/gator/gator_marshaling.c | 24 +- drivers/gator/gator_pmu.c | 242 + drivers/gator/gator_trace_gpu.c | 4 +- drivers/gator/gator_trace_power.c | 13 +- drivers/gator/gator_trace_sched.c | 68 +- drivers/gator/mali/mali_kbase_gator_api.h | 219 - .../gator/mali/mali_mjollnir_profiling_gator_api.h | 2 +- .../gator/mali/mali_utgard_profiling_gator_api.h | 2 +- drivers/gator/mali_midgard.mk | 52 +- tools/gator/daemon/Android.mk | 11 +- tools/gator/daemon/AnnotateListener.cpp | 30 +- tools/gator/daemon/AnnotateListener.h | 16 +- tools/gator/daemon/Application.mk | 2 +- tools/gator/daemon/AtraceDriver.cpp | 127 + tools/gator/daemon/AtraceDriver.h | 39 + tools/gator/daemon/Buffer.cpp | 135 +- tools/gator/daemon/Buffer.h | 43 +- tools/gator/daemon/CCNDriver.cpp | 46 +- tools/gator/daemon/CCNDriver.h | 4 +- tools/gator/daemon/COPYING | 339 + tools/gator/daemon/CPUFreqDriver.cpp | 58 - tools/gator/daemon/CPUFreqDriver.h | 34 - tools/gator/daemon/CapturedXML.cpp | 23 +- tools/gator/daemon/CapturedXML.h | 2 +- tools/gator/daemon/Child.cpp | 152 +- tools/gator/daemon/Child.h | 2 +- tools/gator/daemon/Command.cpp | 94 +- tools/gator/daemon/Command.h | 2 +- tools/gator/daemon/Config.h | 9 +- tools/gator/daemon/ConfigurationXML.cpp | 74 +- tools/gator/daemon/ConfigurationXML.h | 4 +- tools/gator/daemon/Counter.h | 2 +- tools/gator/daemon/DiskIODriver.cpp | 22 +- tools/gator/daemon/DiskIODriver.h | 2 +- tools/gator/daemon/Driver.cpp | 5 +- tools/gator/daemon/Driver.h | 4 +- tools/gator/daemon/DriverSource.cpp | 123 +- tools/gator/daemon/DriverSource.h | 3 +- tools/gator/daemon/DynBuf.cpp | 78 +- tools/gator/daemon/DynBuf.h | 7 +- tools/gator/daemon/EventsXML.cpp | 248 +- tools/gator/daemon/EventsXML.h | 9 +- tools/gator/daemon/ExternalDriver.cpp | 269 + tools/gator/daemon/ExternalDriver.h | 41 + tools/gator/daemon/ExternalSource.cpp | 242 +- tools/gator/daemon/ExternalSource.h | 12 +- tools/gator/daemon/FSDriver.cpp | 32 +- tools/gator/daemon/FSDriver.h | 2 +- tools/gator/daemon/Fifo.cpp | 6 +- tools/gator/daemon/Fifo.h | 2 +- tools/gator/daemon/FtraceDriver.cpp | 496 +- tools/gator/daemon/FtraceDriver.h | 33 +- tools/gator/daemon/FtraceSource.cpp | 158 - tools/gator/daemon/FtraceSource.h | 43 - tools/gator/daemon/HwmonDriver.cpp | 162 +- tools/gator/daemon/HwmonDriver.h | 2 +- tools/gator/daemon/KMod.cpp | 6 +- tools/gator/daemon/KMod.h | 2 +- tools/gator/daemon/LocalCapture.cpp | 32 +- tools/gator/daemon/LocalCapture.h | 2 +- tools/gator/daemon/Logging.cpp | 80 +- tools/gator/daemon/Logging.h | 34 +- tools/gator/daemon/Makefile | 2 +- tools/gator/daemon/MaliVideoDriver.cpp | 32 +- tools/gator/daemon/MaliVideoDriver.h | 3 +- tools/gator/daemon/MemInfoDriver.cpp | 6 +- tools/gator/daemon/MemInfoDriver.h | 2 +- tools/gator/daemon/MidgardDriver.cpp | 325 + tools/gator/daemon/MidgardDriver.h | 37 + tools/gator/daemon/Monitor.cpp | 20 +- tools/gator/daemon/Monitor.h | 2 +- tools/gator/daemon/NetDriver.cpp | 8 +- tools/gator/daemon/NetDriver.h | 2 +- tools/gator/daemon/OlySocket.cpp | 59 +- tools/gator/daemon/OlySocket.h | 10 +- tools/gator/daemon/OlyUtility.cpp | 22 +- tools/gator/daemon/OlyUtility.h | 28 +- tools/gator/daemon/PerfBuffer.cpp | 141 +- tools/gator/daemon/PerfBuffer.h | 5 +- tools/gator/daemon/PerfDriver.cpp | 375 +- tools/gator/daemon/PerfDriver.h | 24 +- tools/gator/daemon/PerfGroup.cpp | 274 +- tools/gator/daemon/PerfGroup.h | 25 +- tools/gator/daemon/PerfSource.cpp | 260 +- tools/gator/daemon/PerfSource.h | 5 +- tools/gator/daemon/PmuXML.cpp | 152 + tools/gator/daemon/PmuXML.h | 29 + tools/gator/daemon/Proc.cpp | 96 +- tools/gator/daemon/Proc.h | 5 +- tools/gator/daemon/Sender.cpp | 45 +- tools/gator/daemon/Sender.h | 5 +- tools/gator/daemon/SessionData.cpp | 264 +- tools/gator/daemon/SessionData.h | 154 +- tools/gator/daemon/SessionXML.cpp | 50 +- tools/gator/daemon/SessionXML.h | 2 +- tools/gator/daemon/Setup.cpp | 232 - tools/gator/daemon/Setup.h | 18 - tools/gator/daemon/Source.cpp | 4 +- tools/gator/daemon/Source.h | 2 +- tools/gator/daemon/StreamlineSetup.cpp | 83 +- tools/gator/daemon/StreamlineSetup.h | 2 +- tools/gator/daemon/TtraceDriver.cpp | 132 + tools/gator/daemon/TtraceDriver.h | 38 + tools/gator/daemon/UEvent.cpp | 9 +- tools/gator/daemon/UEvent.h | 2 +- tools/gator/daemon/UserSpaceSource.cpp | 55 +- tools/gator/daemon/UserSpaceSource.h | 2 +- tools/gator/daemon/c++.cpp | 2 +- tools/gator/daemon/common.mk | 13 +- tools/gator/daemon/defaults.xml | 53 +- tools/gator/daemon/defaults_xml.h | 407 - tools/gator/daemon/escape.c | 2 +- tools/gator/daemon/events-ARM11.xml | 2 +- tools/gator/daemon/events-CCI-400.xml | 8 +- tools/gator/daemon/events-CCI-500.xml | 75 + tools/gator/daemon/events-Cortex-A15.xml | 100 +- tools/gator/daemon/events-Cortex-A17.xml | 79 +- tools/gator/daemon/events-Cortex-A35.xml | 68 + tools/gator/daemon/events-Cortex-A5.xml | 52 +- tools/gator/daemon/events-Cortex-A53.xml | 123 +- tools/gator/daemon/events-Cortex-A57.xml | 64 +- tools/gator/daemon/events-Cortex-A7.xml | 65 +- tools/gator/daemon/events-Cortex-A72.xml | 87 + tools/gator/daemon/events-Cortex-A8.xml | 44 +- tools/gator/daemon/events-Cortex-A9.xml | 70 +- tools/gator/daemon/events-Exynos-M1.xml | 89 + tools/gator/daemon/events-Filesystem.xml | 3 +- tools/gator/daemon/events-Krait-architected.xml | 4 +- tools/gator/daemon/events-L2C-310.xml | 4 +- tools/gator/daemon/events-Linux.xml | 6 +- tools/gator/daemon/events-Mali-4xx.xml | 17 +- tools/gator/daemon/events-Mali-Midgard.xml | 46 +- tools/gator/daemon/events-Mali-Midgard_hw.xml | 71 +- tools/gator/daemon/events-Mali-T60x_hw.xml | 83 +- tools/gator/daemon/events-Mali-T62x_hw.xml | 83 +- tools/gator/daemon/events-Mali-T72x_hw.xml | 74 +- tools/gator/daemon/events-Mali-T76x_hw.xml | 82 +- tools/gator/daemon/events-Mali-T82x_hw.xml | 76 + tools/gator/daemon/events-Mali-T83x_hw.xml | 76 + tools/gator/daemon/events-Mali-T86x_hw.xml | 84 + tools/gator/daemon/events-Mali-T88x_hw.xml | 84 + tools/gator/daemon/events-Mali-V500.xml | 4 +- tools/gator/daemon/events-Other.xml | 33 + tools/gator/daemon/events-Scorpion.xml | 4 +- tools/gator/daemon/events-ScorpionMP.xml | 4 +- tools/gator/daemon/events-atrace.xml | 19 + tools/gator/daemon/events-ftrace.xml | 23 +- tools/gator/daemon/events-ttrace.xml | 17 + tools/gator/daemon/events.xml | 1919 -- tools/gator/daemon/events_xml.h | 20025 ------------------- tools/gator/daemon/k/perf_event.3.12.h | 184 +- tools/gator/daemon/libsensors/access.c | 4 +- tools/gator/daemon/libsensors/access.h | 2 +- tools/gator/daemon/libsensors/conf-lex.c | 78 +- tools/gator/daemon/libsensors/conf-parse.c | 1157 +- tools/gator/daemon/libsensors/conf-parse.h | 75 +- tools/gator/daemon/libsensors/data.c | 2 +- tools/gator/daemon/libsensors/data.h | 2 +- tools/gator/daemon/libsensors/error.c | 27 +- tools/gator/daemon/libsensors/error.h | 2 +- tools/gator/daemon/libsensors/init.c | 8 +- tools/gator/daemon/libsensors/init.h | 2 +- tools/gator/daemon/libsensors/sensors.h | 7 +- tools/gator/daemon/libsensors/sysfs.c | 139 +- tools/gator/daemon/libsensors/sysfs.h | 2 +- tools/gator/daemon/libsensors/version.h | 2 +- tools/gator/daemon/main.cpp | 227 +- tools/gator/daemon/mxml/config.h | 2 +- tools/gator/daemon/mxml/mxml-file.c | 8 +- tools/gator/daemon/pmus.xml | 38 + 202 files changed, 7550 insertions(+), 28097 deletions(-) rename drivers/gator/{LICENSE => COPYING} (100%) delete mode 100644 drivers/gator/gator_events_armv6.c delete mode 100644 drivers/gator/gator_events_armv7.c delete mode 100644 drivers/gator/gator_events_scorpion.c create mode 100644 drivers/gator/gator_pmu.c delete mode 100644 drivers/gator/mali/mali_kbase_gator_api.h create mode 100644 tools/gator/daemon/AtraceDriver.cpp create mode 100644 tools/gator/daemon/AtraceDriver.h create mode 100644 tools/gator/daemon/COPYING delete mode 100644 tools/gator/daemon/CPUFreqDriver.cpp delete mode 100644 tools/gator/daemon/CPUFreqDriver.h create mode 100644 tools/gator/daemon/ExternalDriver.cpp create mode 100644 tools/gator/daemon/ExternalDriver.h delete mode 100644 tools/gator/daemon/FtraceSource.cpp delete mode 100644 tools/gator/daemon/FtraceSource.h create mode 100644 tools/gator/daemon/MidgardDriver.cpp create mode 100644 tools/gator/daemon/MidgardDriver.h create mode 100644 tools/gator/daemon/PmuXML.cpp create mode 100644 tools/gator/daemon/PmuXML.h delete mode 100644 tools/gator/daemon/Setup.cpp delete mode 100644 tools/gator/daemon/Setup.h create mode 100644 tools/gator/daemon/TtraceDriver.cpp create mode 100644 tools/gator/daemon/TtraceDriver.h delete mode 100644 tools/gator/daemon/defaults_xml.h create mode 100644 tools/gator/daemon/events-CCI-500.xml create mode 100644 tools/gator/daemon/events-Cortex-A35.xml create mode 100644 tools/gator/daemon/events-Cortex-A72.xml create mode 100644 tools/gator/daemon/events-Exynos-M1.xml create mode 100644 tools/gator/daemon/events-Mali-T82x_hw.xml create mode 100644 tools/gator/daemon/events-Mali-T83x_hw.xml create mode 100644 tools/gator/daemon/events-Mali-T86x_hw.xml create mode 100644 tools/gator/daemon/events-Mali-T88x_hw.xml create mode 100644 tools/gator/daemon/events-Other.xml create mode 100644 tools/gator/daemon/events-atrace.xml create mode 100644 tools/gator/daemon/events-ttrace.xml delete mode 100644 tools/gator/daemon/events.xml delete mode 100644 tools/gator/daemon/events_xml.h mode change 100644 => 100755 tools/gator/daemon/k/perf_event.3.12.h mode change 100644 => 100755 tools/gator/daemon/libsensors/access.c mode change 100644 => 100755 tools/gator/daemon/libsensors/access.h mode change 100644 => 100755 tools/gator/daemon/libsensors/conf-lex.c mode change 100644 => 100755 tools/gator/daemon/libsensors/conf-parse.c mode change 100644 => 100755 tools/gator/daemon/libsensors/conf-parse.h mode change 100644 => 100755 tools/gator/daemon/libsensors/data.c mode change 100644 => 100755 tools/gator/daemon/libsensors/data.h mode change 100644 => 100755 tools/gator/daemon/libsensors/error.c mode change 100644 => 100755 tools/gator/daemon/libsensors/error.h mode change 100644 => 100755 tools/gator/daemon/libsensors/init.c mode change 100644 => 100755 tools/gator/daemon/libsensors/init.h mode change 100644 => 100755 tools/gator/daemon/libsensors/sensors.h mode change 100644 => 100755 tools/gator/daemon/libsensors/sysfs.c mode change 100644 => 100755 tools/gator/daemon/libsensors/sysfs.h mode change 100644 => 100755 tools/gator/daemon/libsensors/version.h mode change 100644 => 100755 tools/gator/daemon/mxml/config.h mode change 100644 => 100755 tools/gator/daemon/mxml/mxml-file.c create mode 100644 tools/gator/daemon/pmus.xml diff --git a/drivers/gator/LICENSE b/drivers/gator/COPYING similarity index 100% rename from drivers/gator/LICENSE rename to drivers/gator/COPYING diff --git a/drivers/gator/Kconfig b/drivers/gator/Kconfig index b2358bb..f495154 100644 --- a/drivers/gator/Kconfig +++ b/drivers/gator/Kconfig @@ -30,10 +30,18 @@ config GATOR_MALI_MIDGARD endchoice -config GATOR_MALI_PATH +config GATOR_MALI_4XXMP_PATH string "Path to Mali driver" - depends on GATOR_WITH_MALI_SUPPORT + depends on GATOR_MALI_4XXMP default "drivers/gpu/arm/mali400mp" help The gator code adds this to its include path so it can get the Mali trace headers with: #include "linux/mali_linux_trace.h" + +config GATOR_MALI_MIDGARD_PATH + string "Path to Mali driver" + depends on GATOR_MALI_MIDGARD + default "drivers/gpu/arm/midgard" + help + The gator code adds this to its include path so it can get the Mali + trace headers with: #include "linux/mali_linux_trace.h" diff --git a/drivers/gator/Makefile b/drivers/gator/Makefile index 28d2070..0aeeb81 100644 --- a/drivers/gator/Makefile +++ b/drivers/gator/Makefile @@ -12,7 +12,6 @@ gator-y := gator_main.o \ gator_events_meminfo.o \ gator_events_mmapped.o \ gator_events_net.o \ - gator_events_perf_pmu.o \ gator_events_sched.o \ # Convert the old GATOR_WITH_MALI_SUPPORT to the new kernel flags @@ -29,6 +28,14 @@ ifneq ($(GATOR_WITH_MALI_SUPPORT),) ifneq ($(GATOR_MALI_INTERFACE_STYLE),) EXTRA_CFLAGS += -DGATOR_MALI_INTERFACE_STYLE=$(GATOR_MALI_INTERFACE_STYLE) endif + ifneq ($(GATOR_MALI_4XXMP_PATH),) + CONFIG_GATOR_MALI_4XXMP_PATH = $(GATOR_MALI_4XXMP_PATH) + endif + CONFIG_GATOR_MALI_4XXMP_PATH ?= drivers/gpu/arm/mali400mp + ifneq ($(GATOR_MALI_MIDGARD_PATH),) + CONFIG_GATOR_MALI_MIDGARD_PATH = $(GATOR_MALI_MIDGARD_PATH) + endif + CONFIG_GATOR_MALI_MIDGARD_PATH ?= drivers/gpu/arm/midgard endif ifeq ($(CONFIG_GATOR_WITH_MALI_SUPPORT),y) @@ -41,8 +48,11 @@ ifeq ($(CONFIG_GATOR_WITH_MALI_SUPPORT),y) endif gator-y += gator_events_mali_common.o - ifneq ($(CONFIG_GATOR_MALI_PATH),) - ccflags-y += -I$(CONFIG_GATOR_MALI_PATH) + ifneq ($(CONFIG_GATOR_MALI_4XXMP_PATH),) + ccflags-$(CONFIG_GATOR_MALI_4XXMP) += -I$(CONFIG_GATOR_MALI_4XXMP_PATH) + endif + ifneq ($(CONFIG_GATOR_MALI_MIDGARD_PATH),) + ccflags-$(CONFIG_GATOR_MALI_MIDGARD) += -I$(CONFIG_GATOR_MALI_MIDGARD_PATH) endif ccflags-$(CONFIG_GATOR_MALI_4XXMP) += -DMALI_SUPPORT=MALI_4xx ccflags-$(CONFIG_GATOR_MALI_MIDGARD) += -DMALI_SUPPORT=MALI_MIDGARD @@ -56,13 +66,26 @@ EXTRA_CFLAGS += -DGATOR_TEST=$(GATOR_TEST) OLD_BLOCK_RQ_COMPLETE := $(shell grep -A3 block_rq_complete $(srctree)/include/trace/events/block.h | grep nr_bytes -q; echo $$?) EXTRA_CFLAGS += -DOLD_BLOCK_RQ_COMPLETE=$(OLD_BLOCK_RQ_COMPLETE) -gator-$(CONFIG_ARM) += gator_events_armv6.o \ - gator_events_armv7.o \ - gator_events_l2c-310.o \ - gator_events_scorpion.o +gator-$(CONFIG_ARM) += gator_events_l2c-310.o gator-$(CONFIG_ARM64) += +$(obj)/gator_main.o: $(obj)/gator_src_md5.h + +clean-files := gator_src_md5.h + +# Note, in the recipe below we use "cd $(srctree) && cd $(src)" rather than +# "cd $(srctree)/$(src)" because under DKMS $(src) is an absolute path, and we +# can't just use $(src) because for normal kernel builds this is relative to +# $(srctree) + + chk_events.h = : + quiet_chk_events.h = echo ' CHK $@' +silent_chk_events.h = : +$(obj)/gator_src_md5.h: FORCE + @$($(quiet)chk_events.h) + $(Q)cd $(srctree) && cd $(src) ; $(CONFIG_SHELL) -c "echo 'static char *gator_src_md5 = \"'\`ls *.c *.h mali/*.h | grep -Ev '^(gator_src_md5\.h|gator\.mod\.c)$$' | LC_ALL=C sort | xargs cat | md5sum | cut -b 1-32\`'\";'" > $(abspath $@) + else all: @@ -73,7 +96,7 @@ all: $(error) clean: - rm -f *.o .*.cmd modules.order Module.symvers gator.ko gator.mod.c + rm -f *.o .*.cmd gator_src_md5.h modules.order Module.symvers gator.ko gator.mod.c rm -rf .tmp_versions endif diff --git a/drivers/gator/gator.h b/drivers/gator/gator.h index 5cc73a3..d779c67 100644 --- a/drivers/gator/gator.h +++ b/drivers/gator/gator.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -14,32 +14,14 @@ #include #include -#define GATOR_PERF_SUPPORT (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) -#define GATOR_PERF_PMU_SUPPORT (GATOR_PERF_SUPPORT && defined(CONFIG_PERF_EVENTS) && (!(defined(__arm__) || defined(__aarch64__)) || defined(CONFIG_HW_PERF_EVENTS))) -#define GATOR_NO_PERF_SUPPORT (!(GATOR_PERF_SUPPORT)) -#define GATOR_CPU_FREQ_SUPPORT ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) && defined(CONFIG_CPU_FREQ)) +#define GATOR_PERF_PMU_SUPPORT (defined(CONFIG_PERF_EVENTS) && (!(defined(__arm__) || defined(__aarch64__)) || defined(CONFIG_HW_PERF_EVENTS))) +#define GATOR_CPU_FREQ_SUPPORT defined(CONFIG_CPU_FREQ) #define GATOR_IKS_SUPPORT defined(CONFIG_BL_SWITCHER) /* cpu ids */ -#define ARM1136 0xb36 -#define ARM1156 0xb56 -#define ARM1176 0xb76 -#define ARM11MPCORE 0xb02 -#define CORTEX_A5 0xc05 -#define CORTEX_A7 0xc07 -#define CORTEX_A8 0xc08 -#define CORTEX_A9 0xc09 -#define CORTEX_A15 0xc0f -#define CORTEX_A17 0xc0e -#define SCORPION 0x00f -#define SCORPIONMP 0x02d -#define KRAITSIM 0x049 -#define KRAIT 0x04d -#define KRAIT_S4_PRO 0x06f -#define CORTEX_A53 0xd03 -#define CORTEX_A57 0xd07 -#define AARCH64 0xd0f -#define OTHER 0xfff +#define CORTEX_A5 0x41c05 +#define CORTEX_A9 0x41c09 +#define OTHER 0xfffff /* gpu enums */ #define MALI_4xx 1 @@ -47,20 +29,6 @@ #define MAXSIZE_CORE_NAME 32 -struct gator_cpu { - const int cpuid; - /* Human readable name */ - const char core_name[MAXSIZE_CORE_NAME]; - /* gatorfs event and Perf PMU name */ - const char *const pmnc_name; - /* compatible from Documentation/devicetree/bindings/arm/cpus.txt */ - const char *const dt_name; - const int pmnc_counters; -}; - -const struct gator_cpu *gator_find_cpu_by_cpuid(const u32 cpuid); -const struct gator_cpu *gator_find_cpu_by_pmu_name(const char *const name); - /****************************************************************************** * Filesystem ******************************************************************************/ @@ -76,15 +44,8 @@ int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root, /****************************************************************************** * Tracepoints ******************************************************************************/ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) -# error Kernels prior to 2.6.32 not supported -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) -# define GATOR_DEFINE_PROBE(probe_name, proto) \ - static void probe_##probe_name(PARAMS(proto)) -# define GATOR_REGISTER_TRACE(probe_name) \ - register_trace_##probe_name(probe_##probe_name) -# define GATOR_UNREGISTER_TRACE(probe_name) \ - unregister_trace_##probe_name(probe_##probe_name) +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) +# error Kernels prior to 3.4 not supported. DS-5 v5.21 and earlier supported 2.6.32 and later. #elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) # define GATOR_DEFINE_PROBE(probe_name, proto) \ static void probe_##probe_name(void *data, PARAMS(proto)) @@ -106,6 +67,7 @@ int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root, * Events ******************************************************************************/ struct gator_interface { + const char *const name; /* Complementary function to init */ void (*shutdown)(void); int (*create_files)(struct super_block *sb, struct dentry *root); @@ -119,17 +81,16 @@ struct gator_interface { /* called in process context but may not be running on core 'cpu' */ void (*offline_dispatch)(int cpu, bool migrate); int (*read)(int **buffer, bool sched_switch); - int (*read64)(long long **buffer); + int (*read64)(long long **buffer, bool sched_switch); int (*read_proc)(long long **buffer, struct task_struct *); struct list_head list; }; +u64 gator_get_time(void); int gator_events_install(struct gator_interface *interface); int gator_events_get_key(void); u32 gator_cpuid(void); -void gator_backtrace_handler(struct pt_regs *const regs); - void gator_marshal_activity_switch(int core, int key, int activity, int pid); #if !GATOR_IKS_SUPPORT diff --git a/drivers/gator/gator_annotate.c b/drivers/gator/gator_annotate.c index ff9a3ce..cc9ae02 100644 --- a/drivers/gator/gator_annotate.c +++ b/drivers/gator/gator_annotate.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 diff --git a/drivers/gator/gator_annotate_kernel.c b/drivers/gator/gator_annotate_kernel.c index 69471f9..54e8e86 100644 --- a/drivers/gator/gator_annotate_kernel.c +++ b/drivers/gator/gator_annotate_kernel.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012-2014. All rights reserved. + * Copyright (C) ARM Limited 2012-2015. 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 diff --git a/drivers/gator/gator_backtrace.c b/drivers/gator/gator_backtrace.c index 76c941d..ae63432 100644 --- a/drivers/gator/gator_backtrace.c +++ b/drivers/gator/gator_backtrace.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -118,6 +118,9 @@ static void arm_backtrace_eabi(int cpu, struct pt_regs *const regs, unsigned int } #if defined(__arm__) || defined(__aarch64__) + +#include + static int report_trace(struct stackframe *frame, void *d) { unsigned int *depth = d, cookie = NO_COOKIE; diff --git a/drivers/gator/gator_buffer.c b/drivers/gator/gator_buffer.c index 910d5aa..f335457 100644 --- a/drivers/gator/gator_buffer.c +++ b/drivers/gator/gator_buffer.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 diff --git a/drivers/gator/gator_buffer_write.c b/drivers/gator/gator_buffer_write.c index 654ec60..b731e6a 100644 --- a/drivers/gator/gator_buffer_write.c +++ b/drivers/gator/gator_buffer_write.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 diff --git a/drivers/gator/gator_cookies.c b/drivers/gator/gator_cookies.c index c43cce8..ee43df3 100644 --- a/drivers/gator/gator_cookies.c +++ b/drivers/gator/gator_cookies.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -7,6 +7,21 @@ * */ +#include + +struct mount { + struct mount *mnt_parent; + struct dentry *mnt_mountpoint; + struct vfsmount mnt; +}; + +static inline struct mount *real_mount(struct vfsmount *mnt) +{ + return container_of(mnt, struct mount, mnt); +} + +#define GET_MNT_ROOT(mount) ((mount)->mnt.mnt_root) + /* must be power of 2 */ #define COOKIEMAP_ENTRIES 1024 /* must be a power of 2 - 512/4 = 128 entries */ @@ -23,6 +38,7 @@ struct cookie_args { }; static DEFINE_PER_CPU(char *, translate_text); +static DEFINE_PER_CPU(char *, scratch); static DEFINE_PER_CPU(uint32_t, cookie_next_key); static DEFINE_PER_CPU(uint64_t *, cookie_keys); static DEFINE_PER_CPU(uint32_t *, cookie_values); @@ -134,9 +150,7 @@ static void translate_buffer_write_args(int cpu, struct task_struct *task, const args = &per_cpu(translate_buffer, cpu)[write]; args->task = task; args->text = text; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) get_task_struct(task); -#endif per_cpu(translate_buffer_write, cpu) = next_write; } @@ -170,9 +184,7 @@ static void wq_cookie_handler(struct work_struct *unused) translate_buffer_read_args(cpu, &args); cookie = get_cookie(cpu, args.task, args.text, true); marshal_link(cookie, args.task->tgid, args.task->pid); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) put_task_struct(args.task); -#endif } } @@ -285,7 +297,7 @@ static uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, return cookie; /* On 64-bit android app_process can be app_process32 or app_process64 */ - if (strncmp(text, APP_PROCESS, sizeof(APP_PROCESS) - 1) == 0) { + if (strstr(text, APP_PROCESS) != NULL) { if (!translate_app_process(&text, cpu, task, from_wq)) return UNRESOLVED_COOKIE; } @@ -305,6 +317,71 @@ static uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, return cookie; } +/* Can't call d_path in interrupt context so create something similar */ +static const char *gator_d_path(const struct path *path, char *buf, int buflen) +{ + struct dentry *dentry = path->dentry; + struct mount *mount = real_mount(path->mnt); + int pos = buflen - 1; + int len; + + buf[pos] = '\0'; + + for (;;) { + if (dentry == NULL) { + pr_err("gator: dentry is null!\n"); + break; + } + if (dentry->d_name.name[0] == '\0') { + pr_err("gator: path is empty string\n"); + break; + } + if (dentry->d_name.name[0] == '/' && dentry->d_name.name[1] == '\0') { + /* Normal operation */ + /* pr_err("gator: path is /\n"); */ + break; + } + + len = strlen(dentry->d_name.name); + if (pos < len) { + pr_err("gator: path is too long\n"); + break; + } + pos -= len; + memcpy(buf + pos, dentry->d_name.name, len); + + if (pos == 0) { + pr_err("gator: no room for slash\n"); + /* Fall back to name only */ + return path->dentry->d_name.name; + } + --pos; + buf[pos] = '/'; + + if (dentry->d_parent == GET_MNT_ROOT(mount)) { + /* pr_err("gator: filesystem is complete, moving to next '%s'\n", buf + pos); */ + dentry = mount->mnt_mountpoint; + mount = mount->mnt_parent; + continue; + } + if (dentry == dentry->d_parent) { + pr_err("gator: parent is self\n"); + break; + } + dentry = dentry->d_parent; + } + + if (pos < 0) { + pr_err("gator: pos is somenow negative\n"); + /* Fall back to name only */ + return path->dentry->d_name.name; + } + + return buf + pos; +} + +#define d_path gator_d_path + static int get_exec_cookie(int cpu, struct task_struct *task) { struct mm_struct *mm = task->mm; @@ -315,7 +392,7 @@ static int get_exec_cookie(int cpu, struct task_struct *task) return NO_COOKIE; if (task && task->mm && task->mm->exe_file) { - text = task->mm->exe_file->f_path.dentry->d_name.name; + text = d_path(&task->mm->exe_file->f_path, per_cpu(scratch, get_physical_cpu()), PAGE_SIZE); return get_cookie(cpu, task, text, false); } @@ -337,7 +414,7 @@ static unsigned long get_address_cookie(int cpu, struct task_struct *task, unsig continue; if (vma->vm_file) { - text = vma->vm_file->f_path.dentry->d_name.name; + text = d_path(&vma->vm_file->f_path, per_cpu(scratch, get_physical_cpu()), PAGE_SIZE); cookie = get_cookie(cpu, task, text, false); *offset = (vma->vm_pgoff << PAGE_SHIFT) + addr - vma->vm_start; } else { @@ -394,6 +471,12 @@ static int cookies_initialize(void) err = -ENOMEM; goto cookie_setup_error; } + + per_cpu(scratch, cpu) = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!per_cpu(scratch, cpu)) { + err = -ENOMEM; + goto cookie_setup_error; + } } /* build CRC32 table */ @@ -414,7 +497,7 @@ static int cookies_initialize(void) gator_crc32_table[i] = crc; } - setup_timer(&app_process_wake_up_timer, app_process_wake_up_handler, 0); + setup_deferrable_timer_on_stack(&app_process_wake_up_timer, app_process_wake_up_handler, 0); cookie_setup_error: return err; @@ -438,6 +521,9 @@ static void cookies_release(void) kfree(per_cpu(translate_text, cpu)); per_cpu(translate_text, cpu) = NULL; + + kfree(per_cpu(scratch, cpu)); + per_cpu(scratch, cpu) = NULL; } del_timer_sync(&app_process_wake_up_timer); diff --git a/drivers/gator/gator_events_armv6.c b/drivers/gator/gator_events_armv6.c deleted file mode 100644 index a157a00..0000000 --- a/drivers/gator/gator_events_armv6.c +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Copyright (C) ARM Limited 2010-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 "gator.h" - -/* gator_events_perf_pmu.c is used if perf is supported */ -#if GATOR_NO_PERF_SUPPORT - -static const char *pmnc_name; - -/* - * Per-CPU PMCR - */ -#define PMCR_E (1 << 0) /* Enable */ -#define PMCR_P (1 << 1) /* Count reset */ -#define PMCR_C (1 << 2) /* Cycle counter reset */ -#define PMCR_OFL_PMN0 (1 << 8) /* Count reg 0 overflow */ -#define PMCR_OFL_PMN1 (1 << 9) /* Count reg 1 overflow */ -#define PMCR_OFL_CCNT (1 << 10) /* Cycle counter overflow */ - -#define PMN0 0 -#define PMN1 1 -#define CCNT 2 -#define CNTMAX (CCNT+1) - -static int pmnc_counters; -static unsigned long pmnc_enabled[CNTMAX]; -static unsigned long pmnc_event[CNTMAX]; -static unsigned long pmnc_key[CNTMAX]; - -static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); - -static inline void armv6_pmnc_write(u32 val) -{ - /* upper 4bits and 7, 11 are write-as-0 */ - val &= 0x0ffff77f; - asm volatile("mcr p15, 0, %0, c15, c12, 0" : : "r" (val)); -} - -static inline u32 armv6_pmnc_read(void) -{ - u32 val; - - asm volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (val)); - return val; -} - -static void armv6_pmnc_reset_counter(unsigned int cnt) -{ - u32 val = 0; - - switch (cnt) { - case CCNT: - asm volatile("mcr p15, 0, %0, c15, c12, 1" : : "r" (val)); - break; - case PMN0: - asm volatile("mcr p15, 0, %0, c15, c12, 2" : : "r" (val)); - break; - case PMN1: - asm volatile("mcr p15, 0, %0, c15, c12, 3" : : "r" (val)); - break; - } -} - -int gator_events_armv6_create_files(struct super_block *sb, struct dentry *root) -{ - struct dentry *dir; - int i; - - pmnc_counters = 3; - - for (i = PMN0; i <= CCNT; i++) { - char buf[40]; - - if (i == CCNT) - snprintf(buf, sizeof(buf), "ARM_%s_ccnt", pmnc_name); - else - snprintf(buf, sizeof(buf), "ARM_%s_cnt%d", pmnc_name, i); - dir = gatorfs_mkdir(sb, root, buf); - if (!dir) - return -1; - gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); - gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); - if (i != CCNT) - gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); - } - - return 0; -} - -static int gator_events_armv6_online(int **buffer, bool migrate) -{ - unsigned int cnt, len = 0, cpu = smp_processor_id(); - u32 pmnc; - - if (armv6_pmnc_read() & PMCR_E) - armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E); - - /* initialize PMNC, reset overflow, D bit, C bit and P bit. */ - armv6_pmnc_write(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT | - PMCR_C | PMCR_P); - - /* configure control register */ - for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) { - unsigned long event; - - if (!pmnc_enabled[cnt]) - continue; - - event = pmnc_event[cnt] & 255; - - /* Set event (if destined for PMNx counters) */ - if (cnt == PMN0) - pmnc |= event << 20; - else if (cnt == PMN1) - pmnc |= event << 12; - - /* Reset counter */ - armv6_pmnc_reset_counter(cnt); - } - armv6_pmnc_write(pmnc | PMCR_E); - - /* return zero values, no need to read as the counters were just reset */ - for (cnt = PMN0; cnt <= CCNT; cnt++) { - if (pmnc_enabled[cnt]) { - per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; - per_cpu(perfCnt, cpu)[len++] = 0; - } - } - - if (buffer) - *buffer = per_cpu(perfCnt, cpu); - - return len; -} - -static int gator_events_armv6_offline(int **buffer, bool migrate) -{ - unsigned int cnt; - - armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E); - for (cnt = PMN0; cnt <= CCNT; cnt++) - armv6_pmnc_reset_counter(cnt); - - return 0; -} - -static void gator_events_armv6_stop(void) -{ - unsigned int cnt; - - for (cnt = PMN0; cnt <= CCNT; cnt++) { - pmnc_enabled[cnt] = 0; - pmnc_event[cnt] = 0; - } -} - -static int gator_events_armv6_read(int **buffer, bool sched_switch) -{ - int cnt, len = 0; - int cpu = smp_processor_id(); - - /* a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled */ - if (!(armv6_pmnc_read() & PMCR_E)) - return 0; - - for (cnt = PMN0; cnt <= CCNT; cnt++) { - if (pmnc_enabled[cnt]) { - u32 value = 0; - - switch (cnt) { - case CCNT: - asm volatile("mrc p15, 0, %0, c15, c12, 1" : "=r" (value)); - break; - case PMN0: - asm volatile("mrc p15, 0, %0, c15, c12, 2" : "=r" (value)); - break; - case PMN1: - asm volatile("mrc p15, 0, %0, c15, c12, 3" : "=r" (value)); - break; - } - armv6_pmnc_reset_counter(cnt); - - per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; - per_cpu(perfCnt, cpu)[len++] = value; - } - } - - if (buffer) - *buffer = per_cpu(perfCnt, cpu); - - return len; -} - -static struct gator_interface gator_events_armv6_interface = { - .create_files = gator_events_armv6_create_files, - .stop = gator_events_armv6_stop, - .online = gator_events_armv6_online, - .offline = gator_events_armv6_offline, - .read = gator_events_armv6_read, -}; - -int gator_events_armv6_init(void) -{ - unsigned int cnt; - - switch (gator_cpuid()) { - case ARM1136: - case ARM1156: - case ARM1176: - pmnc_name = "ARM11"; - break; - case ARM11MPCORE: - pmnc_name = "ARM11MPCore"; - break; - default: - return -1; - } - - for (cnt = PMN0; cnt <= CCNT; cnt++) { - pmnc_enabled[cnt] = 0; - pmnc_event[cnt] = 0; - pmnc_key[cnt] = gator_events_get_key(); - } - - return gator_events_install(&gator_events_armv6_interface); -} - -#endif diff --git a/drivers/gator/gator_events_armv7.c b/drivers/gator/gator_events_armv7.c deleted file mode 100644 index 09c9422..0000000 --- a/drivers/gator/gator_events_armv7.c +++ /dev/null @@ -1,314 +0,0 @@ -/** - * Copyright (C) ARM Limited 2010-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. - */ - -/* Disabling interrupts - * Many of the functions below disable interrupts via local_irq_save(). This disabling of interrupts is done to prevent any race conditions - * between multiple entities (e.g. hrtimer interrupts and event based interrupts) calling the same functions. As accessing the pmu involves - * several steps (disable, select, read, enable), these steps must be performed atomically. Normal synchronization routines cannot be used - * as these functions are being called from interrupt context. - */ - -#include "gator.h" - -/* gator_events_perf_pmu.c is used if perf is supported */ -#if GATOR_NO_PERF_SUPPORT - -/* Per-CPU PMNC: config reg */ -#define PMNC_E (1 << 0) /* Enable all counters */ -#define PMNC_P (1 << 1) /* Reset all counters */ -#define PMNC_C (1 << 2) /* Cycle counter reset */ -#define PMNC_MASK 0x3f /* Mask for writable bits */ - -/* ccnt reg */ -#define CCNT_REG (1 << 31) - -#define CCNT 0 -#define CNT0 1 -#define CNTMAX (6+1) - -static const char *pmnc_name; -static int pmnc_counters; - -static unsigned long pmnc_enabled[CNTMAX]; -static unsigned long pmnc_event[CNTMAX]; -static unsigned long pmnc_key[CNTMAX]; - -static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); - -inline void armv7_pmnc_write(u32 val) -{ - val &= PMNC_MASK; - asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val)); -} - -inline u32 armv7_pmnc_read(void) -{ - u32 val; - - asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val)); - return val; -} - -inline u32 armv7_ccnt_read(u32 reset_value) -{ - unsigned long flags; - u32 newval = -reset_value; - u32 den = CCNT_REG; - u32 val; - - local_irq_save(flags); - asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); /* disable */ - asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); /* read */ - asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (newval)); /* new value */ - asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); /* enable */ - local_irq_restore(flags); - - return val; -} - -inline u32 armv7_cntn_read(unsigned int cnt, u32 reset_value) -{ - unsigned long flags; - u32 newval = -reset_value; - u32 sel = (cnt - CNT0); - u32 den = 1 << sel; - u32 oldval; - - local_irq_save(flags); - asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); /* disable */ - asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (sel)); /* select */ - asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (oldval)); /* read */ - asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (newval)); /* new value */ - asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); /* enable */ - local_irq_restore(flags); - - return oldval; -} - -static inline void armv7_pmnc_disable_interrupt(unsigned int cnt) -{ - u32 val = cnt ? (1 << (cnt - CNT0)) : (1 << 31); - - asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (val)); -} - -inline u32 armv7_pmnc_reset_interrupt(void) -{ - /* Get and reset overflow status flags */ - u32 flags; - - asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (flags)); - flags &= 0x8000003f; - asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (flags)); - return flags; -} - -static inline u32 armv7_pmnc_enable_counter(unsigned int cnt) -{ - u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG; - - asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val)); - return cnt; -} - -static inline u32 armv7_pmnc_disable_counter(unsigned int cnt) -{ - u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG; - - asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val)); - return cnt; -} - -static inline int armv7_pmnc_select_counter(unsigned int cnt) -{ - u32 val = (cnt - CNT0); - - asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val)); - return cnt; -} - -static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val) -{ - if (armv7_pmnc_select_counter(cnt) == cnt) - asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val)); -} - -static int gator_events_armv7_create_files(struct super_block *sb, struct dentry *root) -{ - struct dentry *dir; - int i; - - for (i = 0; i < pmnc_counters; i++) { - char buf[40]; - - if (i == 0) - snprintf(buf, sizeof(buf), "%s_ccnt", pmnc_name); - else - snprintf(buf, sizeof(buf), "%s_cnt%d", pmnc_name, i - 1); - dir = gatorfs_mkdir(sb, root, buf); - if (!dir) - return -1; - gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); - gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); - if (i > 0) - gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); - } - - return 0; -} - -static int gator_events_armv7_online(int **buffer, bool migrate) -{ - unsigned int cnt, len = 0, cpu = smp_processor_id(); - - if (armv7_pmnc_read() & PMNC_E) - armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E); - - /* Initialize & Reset PMNC: C bit and P bit */ - armv7_pmnc_write(PMNC_P | PMNC_C); - - /* Reset overflow flags */ - armv7_pmnc_reset_interrupt(); - - for (cnt = CCNT; cnt < CNTMAX; cnt++) { - unsigned long event; - - if (!pmnc_enabled[cnt]) - continue; - - /* Disable counter */ - armv7_pmnc_disable_counter(cnt); - - event = pmnc_event[cnt] & 255; - - /* Set event (if destined for PMNx counters), we don't need to set the event if it's a cycle count */ - if (cnt != CCNT) - armv7_pmnc_write_evtsel(cnt, event); - - armv7_pmnc_disable_interrupt(cnt); - - /* Reset counter */ - cnt ? armv7_cntn_read(cnt, 0) : armv7_ccnt_read(0); - - /* Enable counter */ - armv7_pmnc_enable_counter(cnt); - } - - /* enable */ - armv7_pmnc_write(armv7_pmnc_read() | PMNC_E); - - /* return zero values, no need to read as the counters were just reset */ - for (cnt = 0; cnt < pmnc_counters; cnt++) { - if (pmnc_enabled[cnt]) { - per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; - per_cpu(perfCnt, cpu)[len++] = 0; - } - } - - if (buffer) - *buffer = per_cpu(perfCnt, cpu); - - return len; -} - -static int gator_events_armv7_offline(int **buffer, bool migrate) -{ - /* disable all counters, including PMCCNTR; overflow IRQs will not be signaled */ - armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E); - - return 0; -} - -static void gator_events_armv7_stop(void) -{ - unsigned int cnt; - - for (cnt = CCNT; cnt < CNTMAX; cnt++) { - pmnc_enabled[cnt] = 0; - pmnc_event[cnt] = 0; - } -} - -static int gator_events_armv7_read(int **buffer, bool sched_switch) -{ - int cnt, len = 0; - int cpu = smp_processor_id(); - - /* a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled */ - if (!(armv7_pmnc_read() & PMNC_E)) - return 0; - - for (cnt = 0; cnt < pmnc_counters; cnt++) { - if (pmnc_enabled[cnt]) { - int value; - - if (cnt == CCNT) - value = armv7_ccnt_read(0); - else - value = armv7_cntn_read(cnt, 0); - per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; - per_cpu(perfCnt, cpu)[len++] = value; - } - } - - if (buffer) - *buffer = per_cpu(perfCnt, cpu); - - return len; -} - -static struct gator_interface gator_events_armv7_interface = { - .create_files = gator_events_armv7_create_files, - .stop = gator_events_armv7_stop, - .online = gator_events_armv7_online, - .offline = gator_events_armv7_offline, - .read = gator_events_armv7_read, -}; - -int gator_events_armv7_init(void) -{ - unsigned int cnt; - - switch (gator_cpuid()) { - case CORTEX_A5: - pmnc_name = "ARMv7_Cortex_A5"; - pmnc_counters = 2; - break; - case CORTEX_A7: - pmnc_name = "ARMv7_Cortex_A7"; - pmnc_counters = 4; - break; - case CORTEX_A8: - pmnc_name = "ARMv7_Cortex_A8"; - pmnc_counters = 4; - break; - case CORTEX_A9: - pmnc_name = "ARMv7_Cortex_A9"; - pmnc_counters = 6; - break; - case CORTEX_A15: - pmnc_name = "ARMv7_Cortex_A15"; - pmnc_counters = 6; - break; - /* ARM Cortex A17 is not supported by version of Linux before 3.0 */ - default: - return -1; - } - - pmnc_counters++; /* CNT[n] + CCNT */ - - for (cnt = CCNT; cnt < CNTMAX; cnt++) { - pmnc_enabled[cnt] = 0; - pmnc_event[cnt] = 0; - pmnc_key[cnt] = gator_events_get_key(); - } - - return gator_events_install(&gator_events_armv7_interface); -} - -#endif diff --git a/drivers/gator/gator_events_block.c b/drivers/gator/gator_events_block.c index a352a54..c822679 100644 --- a/drivers/gator/gator_events_block.c +++ b/drivers/gator/gator_events_block.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -15,11 +15,7 @@ #define BLOCK_TOTAL (BLOCK_RQ_RD+1) -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) -#define EVENTWRITE REQ_RW -#else #define EVENTWRITE REQ_WRITE -#endif static ulong block_rq_wr_enabled; static ulong block_rq_rd_enabled; @@ -142,6 +138,7 @@ static int gator_events_block_read(int **buffer, bool sched_switch) } static struct gator_interface gator_events_block_interface = { + .name = "block", .create_files = gator_events_block_create_files, .start = gator_events_block_start, .stop = gator_events_block_stop, diff --git a/drivers/gator/gator_events_irq.c b/drivers/gator/gator_events_irq.c index 5221aac..acebb28 100644 --- a/drivers/gator/gator_events_irq.c +++ b/drivers/gator/gator_events_irq.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -27,11 +27,7 @@ GATOR_DEFINE_PROBE(irq_handler_exit, atomic_inc(&per_cpu(irqCnt, get_physical_cpu())[HARDIRQ]); } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) -GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(struct softirq_action *h, struct softirq_action *vec)) -#else GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(unsigned int vec_nr)) -#endif { atomic_inc(&per_cpu(irqCnt, get_physical_cpu())[SOFTIRQ]); } @@ -144,6 +140,7 @@ static int gator_events_irq_read(int **buffer, bool sched_switch) } static struct gator_interface gator_events_irq_interface = { + .name = "irq", .create_files = gator_events_irq_create_files, .online = gator_events_irq_online, .start = gator_events_irq_start, diff --git a/drivers/gator/gator_events_l2c-310.c b/drivers/gator/gator_events_l2c-310.c index 73aaac3..de3b383 100644 --- a/drivers/gator/gator_events_l2c-310.c +++ b/drivers/gator/gator_events_l2c-310.c @@ -1,7 +1,7 @@ /** * l2c310 (L2 Cache Controller) event counters for gator * - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -121,6 +121,7 @@ static int gator_events_l2c310_read(int **buffer, bool sched_switch) } static struct gator_interface gator_events_l2c310_interface = { + .name = "l2c-310", .create_files = gator_events_l2c310_create_files, .start = gator_events_l2c310_start, .stop = gator_events_l2c310_stop, diff --git a/drivers/gator/gator_events_mali_4xx.c b/drivers/gator/gator_events_mali_4xx.c index 9cf43fe..fb3909a 100644 --- a/drivers/gator/gator_events_mali_4xx.c +++ b/drivers/gator/gator_events_mali_4xx.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -66,9 +66,7 @@ static unsigned long counter_event[NUMBER_OF_EVENTS]; static unsigned long counter_key[NUMBER_OF_EVENTS]; /* The data we have recorded */ -static u32 counter_data[NUMBER_OF_EVENTS]; -/* The address to sample (or 0 if samples are sent to us) */ -static u32 *counter_address[NUMBER_OF_EVENTS]; +static atomic_t counter_data[NUMBER_OF_EVENTS]; /* An array used to return the data we recorded * as key,value pairs hence the *2 @@ -116,7 +114,7 @@ static inline int is_hw_counter(unsigned int event_id) GATOR_DEFINE_PROBE(mali_hw_counter, TP_PROTO(unsigned int event_id, unsigned int value)) { if (is_hw_counter(event_id)) - counter_data[event_id] = value; + atomic_add(value, &counter_data[event_id]); } GATOR_DEFINE_PROBE(mali_sw_counters, TP_PROTO(pid_t pid, pid_t tid, void *surface_id, unsigned int *counters)) @@ -126,7 +124,7 @@ GATOR_DEFINE_PROBE(mali_sw_counters, TP_PROTO(pid_t pid, pid_t tid, void *surfac /* Copy over the values for those counters which are enabled. */ for (i = FIRST_SW_COUNTER; i <= LAST_SW_COUNTER; i++) { if (counter_enabled[i]) - counter_data[i] = (u32)(counters[i - FIRST_SW_COUNTER]); + atomic_add(counters[i - FIRST_SW_COUNTER], &counter_data[i]); } } @@ -375,22 +373,22 @@ static void mali_counter_initialize(void) if (mali_get_counters) pr_debug("gator: mali online _mali_profiling_get_counters symbol @ %p\n", mali_get_counters); else - pr_debug("gator WARNING: mali _mali_profiling_get_counters symbol not defined\n"); + pr_debug("gator: mali _mali_profiling_get_counters symbol not defined\n"); mali_get_l2_counters = symbol_get(_mali_profiling_get_l2_counters); if (mali_get_l2_counters) pr_debug("gator: mali online _mali_profiling_get_l2_counters symbol @ %p\n", mali_get_l2_counters); else - pr_debug("gator WARNING: mali _mali_profiling_get_l2_counters symbol not defined\n"); + pr_debug("gator: mali _mali_profiling_get_l2_counters symbol not defined\n"); if (!mali_get_counters && !mali_get_l2_counters) { - pr_debug("gator: WARNING: no L2 counters available\n"); + pr_err("gator: no L2 counters available\n"); n_l2_cores = 0; } /* Clear counters in the start */ for (i = 0; i < NUMBER_OF_EVENTS; i++) { - counter_data[i] = 0; + atomic_set(&counter_data[i], 0); prev_set[i] = false; } } @@ -477,7 +475,6 @@ static void stop(void) for (cnt = 0; cnt < NUMBER_OF_EVENTS; cnt++) { counter_enabled[cnt] = 0; counter_event[cnt] = 0; - counter_address[cnt] = NULL; } mali_counter_deinitialize(); @@ -489,10 +486,12 @@ static void dump_counters(unsigned int from_counter, unsigned int to_counter, un for (counter_id = from_counter; counter_id <= to_counter; counter_id++) { if (counter_enabled[counter_id]) { - counter_dump[(*len)++] = counter_key[counter_id]; - counter_dump[(*len)++] = counter_data[counter_id]; + unsigned int value; - counter_data[counter_id] = 0; + counter_dump[(*len)++] = counter_key[counter_id]; + value = atomic_read(&counter_data[counter_id]); + atomic_sub(value, &counter_data[counter_id]); + counter_dump[(*len)++] = value; } } } @@ -564,17 +563,19 @@ static int read(int **buffer, bool sched_switch) * Add in the voltage and frequency counters if enabled. Note * that, since these are actually passed as events, the counter * value should not be cleared. + * + * Intentionally use atomic_read as these are absolute counters */ cnt = COUNTER_FREQUENCY; if (counter_enabled[cnt]) { counter_dump[len++] = counter_key[cnt]; - counter_dump[len++] = counter_data[cnt]; + counter_dump[len++] = atomic_read(&counter_data[cnt]); } cnt = COUNTER_VOLTAGE; if (counter_enabled[cnt]) { counter_dump[len++] = counter_key[cnt]; - counter_dump[len++] = counter_data[cnt]; + counter_dump[len++] = atomic_read(&counter_data[cnt]); } } #endif @@ -586,6 +587,7 @@ static int read(int **buffer, bool sched_switch) } static struct gator_interface gator_events_mali_interface = { + .name = "mali_4xx", .create_files = create_files, .start = start, .stop = stop, @@ -595,8 +597,9 @@ static struct gator_interface gator_events_mali_interface = { extern void gator_events_mali_log_dvfs_event(unsigned int frequency_mhz, unsigned int voltage_mv) { #ifdef DVFS_REPORTED_BY_DDK - counter_data[COUNTER_FREQUENCY] = frequency_mhz; - counter_data[COUNTER_VOLTAGE] = voltage_mv; + /* Intentionally use atomic_set as these are absolute counters */ + atomic_set(&counter_data[COUNTER_FREQUENCY], frequency_mhz); + atomic_set(&counter_data[COUNTER_VOLTAGE], voltage_mv); #endif } @@ -612,8 +615,7 @@ int gator_events_mali_init(void) counter_enabled[cnt] = 0; counter_event[cnt] = 0; counter_key[cnt] = gator_events_get_key(); - counter_address[cnt] = NULL; - counter_data[cnt] = 0; + atomic_set(&counter_data[cnt], 0); } trace_registered = 0; diff --git a/drivers/gator/gator_events_mali_4xx.h b/drivers/gator/gator_events_mali_4xx.h index 976ca8c..8f6a870 100644 --- a/drivers/gator/gator_events_mali_4xx.h +++ b/drivers/gator/gator_events_mali_4xx.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2014. All rights reserved. + * Copyright (C) ARM Limited 2011-2015. 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 diff --git a/drivers/gator/gator_events_mali_common.c b/drivers/gator/gator_events_mali_common.c index 1af87d6..35d2d67 100644 --- a/drivers/gator/gator_events_mali_common.c +++ b/drivers/gator/gator_events_mali_common.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012-2014. All rights reserved. + * Copyright (C) ARM Limited 2012-2015. 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 @@ -11,7 +11,7 @@ extern int gator_mali_create_file_system(const char *mali_name, const char *event_name, struct super_block *sb, struct dentry *root, struct mali_counter *counter, unsigned long *event) { int err; - char buf[255]; + char buf[50]; struct dentry *dir; /* If the counter name is empty ignore it */ @@ -25,31 +25,31 @@ extern int gator_mali_create_file_system(const char *mali_name, const char *even dir = gatorfs_mkdir(sb, root, buf); if (dir == NULL) { - pr_debug("gator: %s: error creating file system for: %s (%s)\n", mali_name, event_name, buf); + pr_err("gator: %s: error creating file system for: %s (%s)\n", mali_name, event_name, buf); return -1; } err = gatorfs_create_ulong(sb, dir, "enabled", &counter->enabled); if (err != 0) { - pr_debug("gator: %s: error calling gatorfs_create_ulong for: %s (%s)\n", mali_name, event_name, buf); + pr_err("gator: %s: error calling gatorfs_create_ulong for: %s (%s)\n", mali_name, event_name, buf); return -1; } err = gatorfs_create_ro_ulong(sb, dir, "key", &counter->key); if (err != 0) { - pr_debug("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)\n", mali_name, event_name, buf); + pr_err("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)\n", mali_name, event_name, buf); return -1; } if (counter->cores != -1) { err = gatorfs_create_ro_ulong(sb, dir, "cores", &counter->cores); if (err != 0) { - pr_debug("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)\n", mali_name, event_name, buf); + pr_err("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)\n", mali_name, event_name, buf); return -1; } } if (event != NULL) { err = gatorfs_create_ulong(sb, dir, "event", event); if (err != 0) { - pr_debug("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)\n", mali_name, event_name, buf); + pr_err("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)\n", mali_name, event_name, buf); return -1; } } diff --git a/drivers/gator/gator_events_mali_common.h b/drivers/gator/gator_events_mali_common.h index e7082e6..a4fc9d7 100644 --- a/drivers/gator/gator_events_mali_common.h +++ b/drivers/gator/gator_events_mali_common.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012-2014. All rights reserved. + * Copyright (C) ARM Limited 2012-2015. 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 diff --git a/drivers/gator/gator_events_mali_midgard.c b/drivers/gator/gator_events_mali_midgard.c index 0aec906..5b84975 100644 --- a/drivers/gator/gator_events_mali_midgard.c +++ b/drivers/gator/gator_events_mali_midgard.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2014. All rights reserved. + * Copyright (C) ARM Limited 2011-2015. 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 @@ -146,6 +146,10 @@ static unsigned int accumulators_data[NUMBER_OF_ACCUMULATORS]; /* Hold the previous timestamp, used to calculate the sample interval. */ static struct timespec prev_timestamp; +static unsigned long long previous_shader_bitmask; +static unsigned long long previous_tiler_bitmask; +static unsigned long long previous_l2_bitmask; + /** * Returns the timespan (in microseconds) between the two specified timestamps. * @@ -209,10 +213,6 @@ GATOR_DEFINE_PROBE(mali_pm_status, TP_PROTO(unsigned int event_id, unsigned long #define L2_PRESENT_LO 0x120 /* (RO) Level 2 cache present bitmap, low word */ #define BIT_AT(value, pos) ((value >> pos) & 1) - static unsigned long long previous_shader_bitmask; - static unsigned long long previous_tiler_bitmask; - static unsigned long long previous_l2_bitmask; - switch (event_id) { case SHADER_PRESENT_LO: { @@ -324,27 +324,27 @@ static int create_files(struct super_block *sb, struct dentry *root) static int register_tracepoints(void) { if (GATOR_REGISTER_TRACE(mali_pm_status)) { - pr_debug("gator: Mali-Midgard: mali_pm_status tracepoint failed to activate\n"); + pr_err("gator: Mali-Midgard: mali_pm_status tracepoint failed to activate\n"); return 0; } if (GATOR_REGISTER_TRACE(mali_page_fault_insert_pages)) { - pr_debug("gator: Mali-Midgard: mali_page_fault_insert_pages tracepoint failed to activate\n"); + pr_err("gator: Mali-Midgard: mali_page_fault_insert_pages tracepoint failed to activate\n"); return 0; } if (GATOR_REGISTER_TRACE(mali_mmu_as_in_use)) { - pr_debug("gator: Mali-Midgard: mali_mmu_as_in_use tracepoint failed to activate\n"); + pr_err("gator: Mali-Midgard: mali_mmu_as_in_use tracepoint failed to activate\n"); return 0; } if (GATOR_REGISTER_TRACE(mali_mmu_as_released)) { - pr_debug("gator: Mali-Midgard: mali_mmu_as_released tracepoint failed to activate\n"); + pr_err("gator: Mali-Midgard: mali_mmu_as_released tracepoint failed to activate\n"); return 0; } if (GATOR_REGISTER_TRACE(mali_total_alloc_pages_change)) { - pr_debug("gator: Mali-Midgard: mali_total_alloc_pages_change tracepoint failed to activate\n"); + pr_err("gator: Mali-Midgard: mali_total_alloc_pages_change tracepoint failed to activate\n"); return 0; } @@ -363,6 +363,10 @@ static int start(void) unsigned int cnt; mali_profiling_control_type *mali_control; + previous_shader_bitmask = 0; + previous_tiler_bitmask = 0; + previous_l2_bitmask = 0; + /* Clean all data for the next capture */ for (cnt = 0; cnt < NUMBER_OF_TIMELINE_EVENTS; cnt++) { timeline_event_starttime[cnt].tv_sec = timeline_event_starttime[cnt].tv_nsec = 0; @@ -546,6 +550,7 @@ static int read(int **buffer, bool sched_switch) } static struct gator_interface gator_events_mali_midgard_interface = { + .name = "mali_midgard", .create_files = create_files, .start = start, .stop = stop, diff --git a/drivers/gator/gator_events_mali_midgard_hw.c b/drivers/gator/gator_events_mali_midgard_hw.c index c8065da..db91c42 100644 --- a/drivers/gator/gator_events_mali_midgard_hw.c +++ b/drivers/gator/gator_events_mali_midgard_hw.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012-2014. All rights reserved. + * Copyright (C) ARM Limited 2012-2015. 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 @@ -18,7 +18,7 @@ /* Mali Midgard DDK includes */ #if defined(MALI_SIMPLE_API) /* Header with wrapper functions to kbase structures and functions */ -#include "mali/mali_kbase_gator_api.h" +#include "mali_kbase_gator_api.h" #elif defined(MALI_DIR_MIDGARD) /* New DDK Directory structure with kernel/drivers/gpu/arm/midgard */ #include "mali_linux_trace.h" @@ -40,6 +40,10 @@ #error MALI_DDK_GATOR_API_VERSION is invalid (must be 1 for r1/r2 DDK, or 2 for r3/r4 DDK, or 3 for r5 and later DDK). #endif +#if !defined(CONFIG_MALI_GATOR_SUPPORT) +#error CONFIG_MALI_GATOR_SUPPORT is required for GPU activity and software counters +#endif + #include "gator_events_mali_common.h" /* @@ -429,7 +433,8 @@ static unsigned int num_hardware_counters_enabled; static struct mali_counter *counters; /* An array used to return the data we recorded as key,value pairs */ -static int *counter_dump; +static long long *counter_dump; +static uint64_t last_read_time; extern struct mali_counter mali_activity[3]; @@ -513,31 +518,6 @@ static void clean_symbols(void) #endif } -/** - * Determines whether a read should take place - * @param current_time The current time, obtained from getnstimeofday() - * @param prev_time_s The number of seconds at the previous read attempt. - * @param next_read_time_ns The time (in ns) when the next read should be allowed. - * - * Note that this function has been separated out here to allow it to be tested. - */ -static int is_read_scheduled(const struct timespec *current_time, u32 *prev_time_s, s32 *next_read_time_ns) -{ - /* If the current ns count rolls over a second, roll the next read time too. */ - if (current_time->tv_sec != *prev_time_s) - *next_read_time_ns = *next_read_time_ns - NSEC_PER_SEC; - - /* Abort the read if the next read time has not arrived. */ - if (current_time->tv_nsec < *next_read_time_ns) - return 0; - - /* Set the next read some fixed time after this one, and update the read timestamp. */ - *next_read_time_ns = current_time->tv_nsec + READ_INTERVAL_NSEC; - - *prev_time_s = current_time->tv_sec; - return 1; -} - static int start(void) { #if MALI_DDK_GATOR_API_VERSION != 3 @@ -548,6 +528,8 @@ static int start(void) #endif int cnt; + last_read_time = 0; + #if MALI_DDK_GATOR_API_VERSION == 3 /* Setup HW counters */ num_hardware_counters_enabled = 0; @@ -616,14 +598,14 @@ static int start(void) /* If we already got a context, fail */ if (kbcontext) { - pr_debug("gator: Mali-Midgard: error context already present\n"); + pr_err("gator: Mali-Midgard: error context already present\n"); goto out; } /* kbcontext will only be valid after all the Mali symbols are loaded successfully */ kbcontext = kbase_create_context_symbol(kbdevice); if (!kbcontext) { - pr_debug("gator: Mali-Midgard: error creating kbase context\n"); + pr_err("gator: Mali-Midgard: error creating kbase context\n"); goto out; } @@ -646,7 +628,7 @@ static int start(void) kernel_dump_buffer = kbase_va_alloc_symbol(kbcontext, 4096, &kernel_dump_buffer_handle); #endif if (!kernel_dump_buffer) { - pr_debug("gator: Mali-Midgard: error trying to allocate va\n"); + pr_err("gator: Mali-Midgard: error trying to allocate va\n"); goto destroy_context; } @@ -661,7 +643,7 @@ static int start(void) /* Use kbase API to enable hardware counters and provide dump buffer */ err = kbase_instr_hwcnt_enable_symbol(kbcontext, &setup); if (err != MALI_ERROR_NONE) { - pr_debug("gator: Mali-Midgard: can't setup hardware counters\n"); + pr_err("gator: Mali-Midgard: can't setup hardware counters\n"); goto free_buffer; } pr_debug("gator: Mali-Midgard: hardware counters enabled\n"); @@ -748,12 +730,12 @@ static int read_counter(const int cnt, const int len, const struct mali_counter { const int block = GET_HW_BLOCK(cnt); const int counter_offset = GET_COUNTER_OFFSET(cnt); + u32 value = 0; #if MALI_DDK_GATOR_API_VERSION == 3 const char *block_base_address = (char *)in_out_info->kernel_dump_buffer; int i; int shader_core_count = 0; - u32 value = 0; for (i = 0; i < in_out_info->nr_hwc_blocks; i++) { if (block == in_out_info->hwc_layout[i]) { @@ -766,6 +748,12 @@ static int read_counter(const int cnt, const int len, const struct mali_counter if (shader_core_count > 1) value /= shader_core_count; #else + const unsigned int vithar_blocks[] = { + 0x700, /* VITHAR_JOB_MANAGER, Block 0 */ + 0x400, /* VITHAR_TILER, Block 1 */ + 0x000, /* VITHAR_SHADER_CORE, Block 2 */ + 0x500 /* VITHAR_MEMORY_SYSTEM, Block 3 */ + }; const char *block_base_address = (char *)kernel_dump_buffer + vithar_blocks[block]; /* If counter belongs to shader block need to take into account all cores */ @@ -803,77 +791,80 @@ static int read_counter(const int cnt, const int len, const struct mali_counter return 2; } -static int read(int **buffer, bool sched_switch) +static int read(long long **buffer, bool sched_switch) { int cnt; int len = 0; uint32_t success; - struct timespec current_time; - static u32 prev_time_s; - static s32 next_read_time_ns; + uint64_t curr_time; if (!on_primary_core() || sched_switch) return 0; - getnstimeofday(¤t_time); + /* + * Report the HW counters + * Only process hardware counters if at least one of the hardware counters is enabled. + */ + if (num_hardware_counters_enabled <= 0) + return 0; + + curr_time = gator_get_time(); /* * Discard reads unless a respectable time has passed. This * reduces the load on the GPU without sacrificing accuracy on * the Streamline display. */ - if (!is_read_scheduled(¤t_time, &prev_time_s, &next_read_time_ns)) + if (curr_time - last_read_time < READ_INTERVAL_NSEC) return 0; - /* - * Report the HW counters - * Only process hardware counters if at least one of the hardware counters is enabled. - */ - if (num_hardware_counters_enabled > 0) { -#if MALI_DDK_GATOR_API_VERSION != 3 - const unsigned int vithar_blocks[] = { - 0x700, /* VITHAR_JOB_MANAGER, Block 0 */ - 0x400, /* VITHAR_TILER, Block 1 */ - 0x000, /* VITHAR_SHADER_CORE, Block 2 */ - 0x500 /* VITHAR_MEMORY_SYSTEM, Block 3 */ - }; -#endif - #if MALI_DDK_GATOR_API_VERSION == 3 - if (!handles) - return -1; + if (!handles) + return -1; - /* Mali symbols can be called safely since a kbcontext is valid */ - if (kbase_gator_instr_hwcnt_dump_complete_symbol(handles, &success) == MALI_TRUE) { + /* Mali symbols can be called safely since a kbcontext is valid */ + if (kbase_gator_instr_hwcnt_dump_complete_symbol(handles, &success)) { #else - if (!kbcontext) - return -1; + if (!kbcontext) + return -1; - /* Mali symbols can be called safely since a kbcontext is valid */ - if (kbase_instr_hwcnt_dump_complete_symbol(kbcontext, &success) == MALI_TRUE) { + /* Mali symbols can be called safely since a kbcontext is valid */ + if (kbase_instr_hwcnt_dump_complete_symbol(kbcontext, &success)) { #endif - kbase_device_busy = false; + kbase_device_busy = false; + + /* + * If last_read_time is zero, then this result is from a previous + * capture or in error. + */ + if (success && last_read_time > 0) { + /* Backdate these events to when they were requested */ + counter_dump[len++] = 0; + counter_dump[len++] = last_read_time; - if (success == MALI_TRUE) { - /* Cycle through hardware counters and accumulate totals */ - for (cnt = 0; cnt < number_of_hardware_counters; cnt++) { - const struct mali_counter *counter = &counters[cnt]; + /* Cycle through hardware counters and accumulate totals */ + for (cnt = 0; cnt < number_of_hardware_counters; cnt++) { + const struct mali_counter *counter = &counters[cnt]; - if (counter->enabled) - len += read_counter(cnt, len, counter); - } + if (counter->enabled) + len += read_counter(cnt, len, counter); } + + /* Restore the timestamp */ + counter_dump[len++] = 0; + counter_dump[len++] = curr_time; } + } - if (!kbase_device_busy) { - kbase_device_busy = true; + if (!kbase_device_busy) { + kbase_device_busy = true; + last_read_time = curr_time; #if MALI_DDK_GATOR_API_VERSION == 3 - kbase_gator_instr_hwcnt_dump_irq_symbol(handles); + kbase_gator_instr_hwcnt_dump_irq_symbol(handles); #else - kbase_instr_hwcnt_dump_irq_symbol(kbcontext); + kbase_instr_hwcnt_dump_irq_symbol(kbcontext); #endif - } } /* Update the buffer */ @@ -919,7 +910,7 @@ static void shutdown(void) hardware_counter_names = NULL; if (kbase_gator_hwcnt_term_names_symbol != NULL) { kbase_gator_hwcnt_term_names_symbol(); - pr_err("Released symbols\n"); + pr_debug("gator: Released symbols\n"); } SYMBOL_CLEANUP(kbase_gator_hwcnt_term_names); @@ -927,11 +918,12 @@ static void shutdown(void) } static struct gator_interface gator_events_mali_midgard_interface = { + .name = "mali_midgard_hw", .shutdown = shutdown, .create_files = create_files, .start = start, .stop = stop, - .read = read + .read64 = read }; int gator_events_mali_midgard_hw_init(void) @@ -968,7 +960,7 @@ int gator_events_mali_midgard_hw_init(void) #endif counters = kmalloc(sizeof(*counters)*number_of_hardware_counters, GFP_KERNEL); - counter_dump = kmalloc(sizeof(*counter_dump)*number_of_hardware_counters*2, GFP_KERNEL); + counter_dump = kmalloc(sizeof(*counter_dump)*number_of_hardware_counters*2 + 4, GFP_KERNEL); gator_mali_initialise_counters(mali_activity, ARRAY_SIZE(mali_activity)); gator_mali_initialise_counters(counters, number_of_hardware_counters); diff --git a/drivers/gator/gator_events_mali_midgard_hw_test.c b/drivers/gator/gator_events_mali_midgard_hw_test.c index 31a91e1..9fd44bc 100644 --- a/drivers/gator/gator_events_mali_midgard_hw_test.c +++ b/drivers/gator/gator_events_mali_midgard_hw_test.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012-2014. All rights reserved. + * Copyright (C) ARM Limited 2012-2015. 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 @@ -10,46 +10,3 @@ /** * Test functions for mali_t600_hw code. */ - -static int is_read_scheduled(const struct timespec *current_time, u32 *prev_time_s, s32 *next_read_time_ns); - -static int test_is_read_scheduled(u32 s, u32 ns, u32 prev_s, s32 next_ns, int expected_result, s32 expected_next_ns) -{ - struct timespec current_time; - u32 prev_time_s = prev_s; - s32 next_read_time_ns = next_ns; - - current_time.tv_sec = s; - current_time.tv_nsec = ns; - - if (is_read_scheduled(¤t_time, &prev_time_s, &next_read_time_ns) != expected_result) { - pr_err("Failed do_read(%u, %u, %u, %d): expected %d\n", s, ns, prev_s, next_ns, expected_result); - return 0; - } - - if (next_read_time_ns != expected_next_ns) { - pr_err("Failed: next_read_ns expected=%d, actual=%d\n", expected_next_ns, next_read_time_ns); - return 0; - } - - return 1; -} - -static void test_all_is_read_scheduled(void) -{ - const int HIGHEST_NS = 999999999; - int n_tests_passed = 0; - - pr_err("gator: running tests on %s\n", __FILE__); - - n_tests_passed += test_is_read_scheduled(0, 0, 0, 0, 1, READ_INTERVAL_NSEC); /* Null time */ - n_tests_passed += test_is_read_scheduled(100, 1000, 0, 0, 1, READ_INTERVAL_NSEC + 1000); /* Initial values */ - - n_tests_passed += test_is_read_scheduled(100, HIGHEST_NS, 100, HIGHEST_NS + 500, 0, HIGHEST_NS + 500); - n_tests_passed += test_is_read_scheduled(101, 0001, 100, HIGHEST_NS + 500, 0, HIGHEST_NS + 500 - NSEC_PER_SEC); - n_tests_passed += test_is_read_scheduled(101, 600, 100, HIGHEST_NS + 500 - NSEC_PER_SEC, 1, 600 + READ_INTERVAL_NSEC); - - n_tests_passed += test_is_read_scheduled(101, 600, 100, HIGHEST_NS + 500, 1, 600 + READ_INTERVAL_NSEC); - - pr_err("gator: %d tests passed\n", n_tests_passed); -} diff --git a/drivers/gator/gator_events_meminfo.c b/drivers/gator/gator_events_meminfo.c index c625ac5..4cea61d 100644 --- a/drivers/gator/gator_events_meminfo.c +++ b/drivers/gator/gator_events_meminfo.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -58,16 +58,14 @@ static ulong proc_enabled[PROC_COUNT]; static ulong proc_keys[PROC_COUNT]; static DEFINE_PER_CPU(long long, proc_buffer[2 * (PROC_COUNT + 3)]); +static void do_read(void); + #if USE_THREAD static int gator_meminfo_func(void *data); static bool gator_meminfo_run; /* Initialize semaphore unlocked to initialize memory values */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) -static DECLARE_MUTEX(gator_meminfo_sem); -#else static DEFINE_SEMAPHORE(gator_meminfo_sem); -#endif static void notify(void) { @@ -78,7 +76,7 @@ static void notify(void) static unsigned int mem_event; static void wq_sched_handler(struct work_struct *wsptr); -DECLARE_WORK(work, wq_sched_handler); +static DECLARE_WORK(work, wq_sched_handler); static struct timer_list meminfo_wake_up_timer; static void meminfo_wake_up_handler(unsigned long unused_data); @@ -89,20 +87,12 @@ static void notify(void) #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) -GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int order)) -#else GATOR_DEFINE_PROBE(mm_page_free, TP_PROTO(struct page *page, unsigned int order)) -#endif { notify(); } -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) -GATOR_DEFINE_PROBE(mm_pagevec_free, TP_PROTO(struct page *page, int cold)) -#else GATOR_DEFINE_PROBE(mm_page_free_batched, TP_PROTO(struct page *page, int cold)) -#endif { notify(); } @@ -162,21 +152,14 @@ static int gator_events_meminfo_start(void) if (meminfo_global_enabled == 0) return 0; -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) - if (GATOR_REGISTER_TRACE(mm_page_free_direct)) -#else if (GATOR_REGISTER_TRACE(mm_page_free)) -#endif goto mm_page_free_exit; -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) - if (GATOR_REGISTER_TRACE(mm_pagevec_free)) -#else if (GATOR_REGISTER_TRACE(mm_page_free_batched)) -#endif goto mm_page_free_batched_exit; if (GATOR_REGISTER_TRACE(mm_page_alloc)) goto mm_page_alloc_exit; + do_read(); #if USE_THREAD /* Start worker thread */ gator_meminfo_run = true; @@ -184,7 +167,7 @@ static int gator_events_meminfo_start(void) if (IS_ERR(kthread_run(gator_meminfo_func, NULL, "gator_meminfo"))) goto kthread_run_exit; #else - setup_timer(&meminfo_wake_up_timer, meminfo_wake_up_handler, 0); + setup_deferrable_timer_on_stack(&meminfo_wake_up_timer, meminfo_wake_up_handler, 0); #endif return 0; @@ -194,17 +177,9 @@ kthread_run_exit: GATOR_UNREGISTER_TRACE(mm_page_alloc); #endif mm_page_alloc_exit: -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) - GATOR_UNREGISTER_TRACE(mm_pagevec_free); -#else GATOR_UNREGISTER_TRACE(mm_page_free_batched); -#endif mm_page_free_batched_exit: -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) - GATOR_UNREGISTER_TRACE(mm_page_free_direct); -#else GATOR_UNREGISTER_TRACE(mm_page_free); -#endif mm_page_free_exit: return -1; } @@ -212,13 +187,8 @@ mm_page_free_exit: static void gator_events_meminfo_stop(void) { if (meminfo_global_enabled) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) - GATOR_UNREGISTER_TRACE(mm_page_free_direct); - GATOR_UNREGISTER_TRACE(mm_pagevec_free); -#else GATOR_UNREGISTER_TRACE(mm_page_free); GATOR_UNREGISTER_TRACE(mm_page_free_batched); -#endif GATOR_UNREGISTER_TRACE(mm_page_alloc); #if USE_THREAD @@ -310,7 +280,7 @@ static void meminfo_wake_up_handler(unsigned long unused_data) #endif -static int gator_events_meminfo_read(long long **buffer) +static int gator_events_meminfo_read(long long **buffer, bool sched_switch) { #if !USE_THREAD static unsigned int last_mem_event; @@ -337,29 +307,6 @@ static int gator_events_meminfo_read(long long **buffer) return meminfo_length; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) - -static inline unsigned long gator_get_mm_counter(struct mm_struct *mm, int member) -{ -#ifdef SPLIT_RSS_COUNTING - long val = atomic_long_read(&mm->rss_stat.count[member]); - - if (val < 0) - val = 0; - return (unsigned long)val; -#else -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0) - return mm->rss_stat.count[member]; -#else - return atomic_long_read(&mm->rss_stat.count[member]); -#endif -#endif -} - -#define get_mm_counter(mm, member) gator_get_mm_counter(mm, member) - -#endif - static int gator_events_meminfo_read_proc(long long **buffer, struct task_struct *task) { struct mm_struct *mm; @@ -384,13 +331,7 @@ static int gator_events_meminfo_read_proc(long long **buffer, struct task_struct /* Derived from task_statm in fs/proc/task_mmu.c */ if (meminfo_enabled[MEMINFO_MEMUSED] || proc_enabled[PROC_SHARE]) { - share = get_mm_counter(mm, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) - file_rss -#else - MM_FILEPAGES -#endif - ); + share = get_mm_counter(mm, MM_FILEPAGES); } /* key of 1 indicates a pid */ @@ -420,13 +361,7 @@ static int gator_events_meminfo_read_proc(long long **buffer, struct task_struct } if (meminfo_enabled[MEMINFO_MEMUSED]) { - value = share + get_mm_counter(mm, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) - anon_rss -#else - MM_ANONPAGES -#endif - ); + value = share + get_mm_counter(mm, MM_ANONPAGES); /* Send resident for this pid */ buf[len++] = meminfo_keys[MEMINFO_MEMUSED]; buf[len++] = value * PAGE_SIZE; @@ -443,6 +378,7 @@ static int gator_events_meminfo_read_proc(long long **buffer, struct task_struct } static struct gator_interface gator_events_meminfo_interface = { + .name = "meminfo", .create_files = gator_events_meminfo_create_files, .start = gator_events_meminfo_start, .stop = gator_events_meminfo_stop, diff --git a/drivers/gator/gator_events_mmapped.c b/drivers/gator/gator_events_mmapped.c index 6b2af99..30ca980 100644 --- a/drivers/gator/gator_events_mmapped.c +++ b/drivers/gator/gator_events_mmapped.c @@ -1,7 +1,7 @@ /* * Example events provider * - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -189,6 +189,7 @@ static int gator_events_mmapped_read(int **buffer, bool sched_switch) } static struct gator_interface gator_events_mmapped_interface = { + .name = "mmapped", .create_files = gator_events_mmapped_create_files, .start = gator_events_mmapped_start, .stop = gator_events_mmapped_stop, diff --git a/drivers/gator/gator_events_net.c b/drivers/gator/gator_events_net.c index d21b4db..0100ae8 100644 --- a/drivers/gator/gator_events_net.c +++ b/drivers/gator/gator_events_net.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -32,12 +32,8 @@ static void get_network_stats(struct work_struct *wsptr) struct net_device *dev; for_each_netdev(&init_net, dev) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) - const struct net_device_stats *stats = dev_get_stats(dev); -#else struct rtnl_link_stats64 temp; const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp); -#endif rx += stats->rx_bytes; tx += stats->tx_bytes; } @@ -45,7 +41,7 @@ static void get_network_stats(struct work_struct *wsptr) tx_total = tx; } -DECLARE_WORK(wq_get_stats, get_network_stats); +static DECLARE_WORK(wq_get_stats, get_network_stats); static void net_wake_up_handler(unsigned long unused_data) { @@ -95,14 +91,10 @@ static int gator_events_net_create_files(struct super_block *sb, struct dentry * static int gator_events_net_start(void) { - get_network_stats(0); + get_network_stats(NULL); netPrev[NETRX] = rx_total; netPrev[NETTX] = tx_total; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) - setup_timer(&net_wake_up_timer, net_wake_up_handler, 0); -#else setup_deferrable_timer_on_stack(&net_wake_up_timer, net_wake_up_handler, 0); -#endif return 0; } @@ -154,6 +146,7 @@ static int gator_events_net_read(int **buffer, bool sched_switch) } static struct gator_interface gator_events_net_interface = { + .name = "net", .create_files = gator_events_net_create_files, .start = gator_events_net_start, .stop = gator_events_net_stop, diff --git a/drivers/gator/gator_events_perf_pmu.c b/drivers/gator/gator_events_perf_pmu.c index 47cf278..f50e93e 100644 --- a/drivers/gator/gator_events_perf_pmu.c +++ b/drivers/gator/gator_events_perf_pmu.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -12,22 +12,13 @@ #if GATOR_PERF_PMU_SUPPORT #include -#ifdef CONFIG_OF -#include -#endif #include #include -extern bool event_based_sampling; - /* Maximum number of per-core counters - currently reserves enough space for two full hardware PMUs for big.LITTLE */ #define CNTMAX 16 -#define CCI_400 4 -#define CCN_5XX 8 /* Maximum number of uncore counters */ -/* + 1 for the cci-400 cycles counter */ -/* + 1 for the CCN-5xx cycles counter */ -#define UCCNT (CCI_400 + 1 + CCN_5XX + 1) +#define UCCNT 32 /* Default to 0 if unable to probe the revision which was the previous behavior */ #define DEFAULT_CCI_REVISION 0 @@ -58,9 +49,9 @@ static struct gator_attr uc_attrs[UCCNT]; static int uc_attr_count; struct gator_event { - int curr; - int prev; - int prev_delta; + uint32_t curr; + uint32_t prev; + uint32_t prev_delta; bool zero; struct perf_event *pevent; struct perf_event_attr *pevent_attr; @@ -106,20 +97,12 @@ static int gator_events_perf_pmu_create_files(struct super_block *sb, struct den return 0; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) -static void ebs_overflow_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs) -#else static void ebs_overflow_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) -#endif { gator_backtrace_handler(regs); } -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) -static void dummy_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs) -#else static void dummy_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) -#endif { /* Required as perf_event_create_kernel_counter() requires an overflow handler, even though all we do is poll */ } @@ -134,10 +117,11 @@ static int gator_events_perf_pmu_online(int **buffer, bool migrate) static void __online_dispatch(int cpu, bool migrate, struct gator_attr *const attr, struct gator_event *const event) { perf_overflow_handler_t handler; + struct perf_event *pevent; event->zero = true; - if (event->pevent != NULL || event->pevent_attr == 0 || migrate) + if (event->pevent != NULL || event->pevent_attr == NULL || migrate) return; if (attr->count > 0) @@ -145,23 +129,19 @@ static void __online_dispatch(int cpu, bool migrate, struct gator_attr *const at else handler = dummy_handler; -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) - event->pevent = perf_event_create_kernel_counter(event->pevent_attr, cpu, 0, handler); -#else - event->pevent = perf_event_create_kernel_counter(event->pevent_attr, cpu, 0, handler, 0); -#endif - if (IS_ERR(event->pevent)) { - pr_debug("gator: unable to online a counter on cpu %d\n", cpu); - event->pevent = NULL; + pevent = perf_event_create_kernel_counter(event->pevent_attr, cpu, NULL, handler, NULL); + if (IS_ERR(pevent)) { + pr_err("gator: unable to online a counter on cpu %d\n", cpu); return; } - if (event->pevent->state != PERF_EVENT_STATE_ACTIVE) { - pr_debug("gator: inactive counter on cpu %d\n", cpu); - perf_event_release_kernel(event->pevent); - event->pevent = NULL; + if (pevent->state != PERF_EVENT_STATE_ACTIVE) { + pr_err("gator: inactive counter on cpu %d\n", cpu); + perf_event_release_kernel(pevent); return; } + + event->pevent = pevent; } static void gator_events_perf_pmu_online_dispatch(int cpu, bool migrate) @@ -315,7 +295,7 @@ static void gator_events_perf_pmu_stop(void) static void __read(int *const len, int cpu, struct gator_attr *const attr, struct gator_event *const event) { - int delta; + uint32_t delta; struct perf_event *const ev = event->pevent; if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) { @@ -341,8 +321,6 @@ static void __read(int *const len, int cpu, struct gator_attr *const attr, struc event->prev_delta = delta; event->prev = event->curr; per_cpu(perf_cnt, cpu)[(*len)++] = attr->key; - if (delta < 0) - delta *= -1; per_cpu(perf_cnt, cpu)[(*len)++] = delta; } } @@ -369,7 +347,7 @@ static int gator_events_perf_pmu_read(int **buffer, bool sched_switch) } static struct gator_interface gator_events_perf_pmu_interface = { - .create_files = gator_events_perf_pmu_create_files, + .name = "perf_pmu", .start = gator_events_perf_pmu_start, .stop = gator_events_perf_pmu_stop, .online = gator_events_perf_pmu_online, @@ -388,118 +366,62 @@ static void __attr_init(struct gator_attr *const attr) attr->key = gator_events_get_key(); } -#ifdef CONFIG_OF - -static const struct of_device_id arm_cci_matches[] = { - {.compatible = "arm,cci-400" }, - {}, -}; - -static int probe_cci_revision(void) -{ - struct device_node *np; - struct resource res; - void __iomem *cci_ctrl_base; - int rev; - int ret = DEFAULT_CCI_REVISION; - - np = of_find_matching_node(NULL, arm_cci_matches); - if (!np) - return ret; - - if (of_address_to_resource(np, 0, &res)) - goto node_put; - - cci_ctrl_base = ioremap(res.start, resource_size(&res)); - - rev = (readl_relaxed(cci_ctrl_base + 0xfe8) >> 4) & 0xf; - - if (rev <= 4) - ret = 0; - else if (rev <= 6) - ret = 1; - - iounmap(cci_ctrl_base); - - node_put: - of_node_put(np); - - return ret; -} - -#else - -static int probe_cci_revision(void) -{ - return DEFAULT_CCI_REVISION; -} - -#endif - -static void gator_events_perf_pmu_uncore_init(const char *const name, const int type, const int count) +static void gator_events_perf_pmu_uncore_init(const struct uncore_pmu *const uncore_pmu, const int type) { int cnt; - snprintf(uc_attrs[uc_attr_count].name, sizeof(uc_attrs[uc_attr_count].name), "%s_ccnt", name); - uc_attrs[uc_attr_count].type = type; - ++uc_attr_count; + if (uncore_pmu->has_cycles_counter) { + if (uc_attr_count < ARRAY_SIZE(uc_attrs)) { + snprintf(uc_attrs[uc_attr_count].name, sizeof(uc_attrs[uc_attr_count].name), "%s_ccnt", uncore_pmu->core_name); + uc_attrs[uc_attr_count].type = type; + } + ++uc_attr_count; + } - for (cnt = 0; cnt < count; ++cnt, ++uc_attr_count) { + for (cnt = 0; cnt < uncore_pmu->pmnc_counters; ++cnt, ++uc_attr_count) { struct gator_attr *const attr = &uc_attrs[uc_attr_count]; - snprintf(attr->name, sizeof(attr->name), "%s_cnt%d", name, cnt); - attr->type = type; - } -} - -static void gator_events_perf_pmu_cci_init(const int type) -{ - const char *cci_name; - - switch (probe_cci_revision()) { - case 0: - cci_name = "CCI_400"; - break; - case 1: - cci_name = "CCI_400-r1"; - break; - default: - pr_debug("gator: unrecognized cci-400 revision\n"); - return; + if (uc_attr_count < ARRAY_SIZE(uc_attrs)) { + snprintf(attr->name, sizeof(attr->name), "%s_cnt%d", uncore_pmu->core_name, cnt); + attr->type = type; + } } - - gator_events_perf_pmu_uncore_init(cci_name, type, CCI_400); } static void gator_events_perf_pmu_cpu_init(const struct gator_cpu *const gator_cpu, const int type) { int cnt; - snprintf(attrs[attr_count].name, sizeof(attrs[attr_count].name), "%s_ccnt", gator_cpu->pmnc_name); - attrs[attr_count].type = type; + if (attr_count < ARRAY_SIZE(attrs)) { + snprintf(attrs[attr_count].name, sizeof(attrs[attr_count].name), "%s_ccnt", gator_cpu->pmnc_name); + attrs[attr_count].type = type; + } ++attr_count; for (cnt = 0; cnt < gator_cpu->pmnc_counters; ++cnt, ++attr_count) { struct gator_attr *const attr = &attrs[attr_count]; - snprintf(attr->name, sizeof(attr->name), "%s_cnt%d", gator_cpu->pmnc_name, cnt); - attr->type = type; + if (attr_count < ARRAY_SIZE(attrs)) { + snprintf(attr->name, sizeof(attr->name), "%s_cnt%d", gator_cpu->pmnc_name, cnt); + attr->type = type; + } } } -int gator_events_perf_pmu_init(void) +static int gator_events_perf_pmu_reread(void) { struct perf_event_attr pea; struct perf_event *pe; const struct gator_cpu *gator_cpu; + const struct uncore_pmu *uncore_pmu; int type; int cpu; int cnt; bool found_cpu = false; - for (cnt = 0; cnt < CNTMAX; cnt++) + for (cnt = 0; cnt < ARRAY_SIZE(attrs); cnt++) __attr_init(&attrs[cnt]); - for (cnt = 0; cnt < UCCNT; cnt++) + for (cnt = 0; cnt < ARRAY_SIZE(uc_attrs); cnt++) __attr_init(&uc_attrs[cnt]); memset(&pea, 0, sizeof(pea)); @@ -513,33 +435,26 @@ int gator_events_perf_pmu_init(void) /* A particular PMU may work on some but not all cores, so try on each core */ pe = NULL; for_each_present_cpu(cpu) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) - pe = perf_event_create_kernel_counter(&pea, cpu, 0, dummy_handler); -#else - pe = perf_event_create_kernel_counter(&pea, cpu, 0, dummy_handler, 0); -#endif + pe = perf_event_create_kernel_counter(&pea, cpu, NULL, dummy_handler, NULL); if (!IS_ERR(pe)) break; } /* Assume that valid PMUs are contiguous */ if (IS_ERR(pe)) { pea.config = 0xff00; -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) - pe = perf_event_create_kernel_counter(&pea, 0, 0, dummy_handler); -#else - pe = perf_event_create_kernel_counter(&pea, 0, 0, dummy_handler, 0); -#endif + pe = perf_event_create_kernel_counter(&pea, 0, NULL, dummy_handler, NULL); if (IS_ERR(pe)) break; } if (pe->pmu != NULL && type == pe->pmu->type) { - if (strcmp("CCI", pe->pmu->name) == 0 || strcmp("CCI_400", pe->pmu->name) == 0 || strcmp("CCI_400-r1", pe->pmu->name) == 0) { - gator_events_perf_pmu_cci_init(type); - } else if (strcmp("ccn", pe->pmu->name) == 0) { - gator_events_perf_pmu_uncore_init("ARM_CCN_5XX", type, CCN_5XX); + pr_notice("gator: perf pmu: %s\n", pe->pmu->name); + if ((uncore_pmu = gator_find_uncore_pmu(pe->pmu->name)) != NULL) { + pr_notice("gator: Adding uncore counters for %s with type %i\n", uncore_pmu->core_name, type); + gator_events_perf_pmu_uncore_init(uncore_pmu, type); } else if ((gator_cpu = gator_find_cpu_by_pmu_name(pe->pmu->name)) != NULL) { found_cpu = true; + pr_notice("gator: Adding cpu counters for %s with type %i\n", gator_cpu->core_name, type); gator_events_perf_pmu_cpu_init(gator_cpu, type); } /* Initialize gator_attrs for dynamic PMUs here */ @@ -549,26 +464,53 @@ int gator_events_perf_pmu_init(void) } if (!found_cpu) { - const struct gator_cpu *const gator_cpu = gator_find_cpu_by_cpuid(gator_cpuid()); + const struct gator_cpu *gator_cpu = gator_find_cpu_by_cpuid(gator_cpuid()); - if (gator_cpu == NULL) +#if defined(__arm__) || defined(__aarch64__) + if (gator_cpu == NULL) { + pr_err("gator: This CPU is not recognized, using the ARM architected counters\n"); + gator_cpu = &(struct gator_cpu) { .pmnc_name = "Other", .cpuid = 0xfffff, .core_name = "Other", .pmnc_counters = 6 }; + } +#else + if (gator_cpu == NULL) { + pr_err("gator: This CPU is not recognized\n"); return -1; + } +#endif + pr_notice("gator: Adding cpu counters (based on cpuid) for %s\n", gator_cpu->pmnc_name); gator_events_perf_pmu_cpu_init(gator_cpu, PERF_TYPE_RAW); } /* Initialize gator_attrs for non-dynamic PMUs here */ if (attr_count > CNTMAX) { - pr_err("gator: Too many perf counters\n"); + pr_err("gator: Too many perf counters, please increase CNTMAX\n"); return -1; } if (uc_attr_count > UCCNT) { - pr_err("gator: Too many perf uncore counters\n"); + pr_err("gator: Too many perf uncore counters, please increase UCCNT\n"); return -1; } + return 0; +} + +int gator_events_perf_pmu_init(void) +{ return gator_events_install(&gator_events_perf_pmu_interface); } +#else + +static int gator_events_perf_pmu_reread(void) +{ + return 0; +} + +static int gator_events_perf_pmu_create_files(struct super_block *sb, struct dentry *root) +{ + return 0; +} + #endif diff --git a/drivers/gator/gator_events_sched.c b/drivers/gator/gator_events_sched.c index 637107d..6bab53c 100644 --- a/drivers/gator/gator_events_sched.c +++ b/drivers/gator/gator_events_sched.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -18,10 +18,10 @@ static ulong sched_switch_key; static DEFINE_PER_CPU(int[SCHED_TOTAL], schedCnt); static DEFINE_PER_CPU(int[SCHED_TOTAL * 2], schedGet); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) -GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next)) -#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next)) +#else +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next)) #endif { unsigned long flags; @@ -97,6 +97,7 @@ static int gator_events_sched_read(int **buffer, bool sched_switch) } static struct gator_interface gator_events_sched_interface = { + .name = "sched", .create_files = gator_events_sched_create_files, .start = gator_events_sched_start, .stop = gator_events_sched_stop, diff --git a/drivers/gator/gator_events_scorpion.c b/drivers/gator/gator_events_scorpion.c deleted file mode 100644 index 4921936..0000000 --- a/drivers/gator/gator_events_scorpion.c +++ /dev/null @@ -1,674 +0,0 @@ -/** - * Copyright (C) ARM Limited 2011-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 "gator.h" - -/* gator_events_perf_pmu.c is used if perf is supported */ -#if GATOR_NO_PERF_SUPPORT - -static const char *pmnc_name; -static int pmnc_counters; - -/* Per-CPU PMNC: config reg */ -#define PMNC_E (1 << 0) /* Enable all counters */ -#define PMNC_P (1 << 1) /* Reset all counters */ -#define PMNC_C (1 << 2) /* Cycle counter reset */ -#define PMNC_D (1 << 3) /* CCNT counts every 64th cpu cycle */ -#define PMNC_X (1 << 4) /* Export to ETM */ -#define PMNC_DP (1 << 5) /* Disable CCNT if non-invasive debug */ -#define PMNC_MASK 0x3f /* Mask for writable bits */ - -/* ccnt reg */ -#define CCNT_REG (1 << 31) - -#define CCNT 0 -#define CNT0 1 -#define CNTMAX (4+1) - -static unsigned long pmnc_enabled[CNTMAX]; -static unsigned long pmnc_event[CNTMAX]; -static unsigned long pmnc_key[CNTMAX]; - -static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); - -enum scorpion_perf_types { - SCORPION_ICACHE_EXPL_INV = 0x4c, - SCORPION_ICACHE_MISS = 0x4d, - SCORPION_ICACHE_ACCESS = 0x4e, - SCORPION_ICACHE_CACHEREQ_L2 = 0x4f, - SCORPION_ICACHE_NOCACHE_L2 = 0x50, - SCORPION_HIQUP_NOPED = 0x51, - SCORPION_DATA_ABORT = 0x52, - SCORPION_IRQ = 0x53, - SCORPION_FIQ = 0x54, - SCORPION_ALL_EXCPT = 0x55, - SCORPION_UNDEF = 0x56, - SCORPION_SVC = 0x57, - SCORPION_SMC = 0x58, - SCORPION_PREFETCH_ABORT = 0x59, - SCORPION_INDEX_CHECK = 0x5a, - SCORPION_NULL_CHECK = 0x5b, - SCORPION_EXPL_ICIALLU = 0x5c, - SCORPION_IMPL_ICIALLU = 0x5d, - SCORPION_NONICIALLU_BTAC_INV = 0x5e, - SCORPION_ICIMVAU_IMPL_ICIALLU = 0x5f, - SCORPION_SPIPE_ONLY_CYCLES = 0x60, - SCORPION_XPIPE_ONLY_CYCLES = 0x61, - SCORPION_DUAL_CYCLES = 0x62, - SCORPION_DISPATCH_ANY_CYCLES = 0x63, - SCORPION_FIFO_FULLBLK_CMT = 0x64, - SCORPION_FAIL_COND_INST = 0x65, - SCORPION_PASS_COND_INST = 0x66, - SCORPION_ALLOW_VU_CLK = 0x67, - SCORPION_VU_IDLE = 0x68, - SCORPION_ALLOW_L2_CLK = 0x69, - SCORPION_L2_IDLE = 0x6a, - SCORPION_DTLB_IMPL_INV_SCTLR_DACR = 0x6b, - SCORPION_DTLB_EXPL_INV = 0x6c, - SCORPION_DTLB_MISS = 0x6d, - SCORPION_DTLB_ACCESS = 0x6e, - SCORPION_ITLB_MISS = 0x6f, - SCORPION_ITLB_IMPL_INV = 0x70, - SCORPION_ITLB_EXPL_INV = 0x71, - SCORPION_UTLB_D_MISS = 0x72, - SCORPION_UTLB_D_ACCESS = 0x73, - SCORPION_UTLB_I_MISS = 0x74, - SCORPION_UTLB_I_ACCESS = 0x75, - SCORPION_UTLB_INV_ASID = 0x76, - SCORPION_UTLB_INV_MVA = 0x77, - SCORPION_UTLB_INV_ALL = 0x78, - SCORPION_S2_HOLD_RDQ_UNAVAIL = 0x79, - SCORPION_S2_HOLD = 0x7a, - SCORPION_S2_HOLD_DEV_OP = 0x7b, - SCORPION_S2_HOLD_ORDER = 0x7c, - SCORPION_S2_HOLD_BARRIER = 0x7d, - SCORPION_VIU_DUAL_CYCLE = 0x7e, - SCORPION_VIU_SINGLE_CYCLE = 0x7f, - SCORPION_VX_PIPE_WAR_STALL_CYCLES = 0x80, - SCORPION_VX_PIPE_WAW_STALL_CYCLES = 0x81, - SCORPION_VX_PIPE_RAW_STALL_CYCLES = 0x82, - SCORPION_VX_PIPE_LOAD_USE_STALL = 0x83, - SCORPION_VS_PIPE_WAR_STALL_CYCLES = 0x84, - SCORPION_VS_PIPE_WAW_STALL_CYCLES = 0x85, - SCORPION_VS_PIPE_RAW_STALL_CYCLES = 0x86, - SCORPION_EXCEPTIONS_INV_OPERATION = 0x87, - SCORPION_EXCEPTIONS_DIV_BY_ZERO = 0x88, - SCORPION_COND_INST_FAIL_VX_PIPE = 0x89, - SCORPION_COND_INST_FAIL_VS_PIPE = 0x8a, - SCORPION_EXCEPTIONS_OVERFLOW = 0x8b, - SCORPION_EXCEPTIONS_UNDERFLOW = 0x8c, - SCORPION_EXCEPTIONS_DENORM = 0x8d, -#ifdef CONFIG_ARCH_MSM_SCORPIONMP - SCORPIONMP_NUM_BARRIERS = 0x8e, - SCORPIONMP_BARRIER_CYCLES = 0x8f, -#else - SCORPION_BANK_AB_HIT = 0x8e, - SCORPION_BANK_AB_ACCESS = 0x8f, - SCORPION_BANK_CD_HIT = 0x90, - SCORPION_BANK_CD_ACCESS = 0x91, - SCORPION_BANK_AB_DSIDE_HIT = 0x92, - SCORPION_BANK_AB_DSIDE_ACCESS = 0x93, - SCORPION_BANK_CD_DSIDE_HIT = 0x94, - SCORPION_BANK_CD_DSIDE_ACCESS = 0x95, - SCORPION_BANK_AB_ISIDE_HIT = 0x96, - SCORPION_BANK_AB_ISIDE_ACCESS = 0x97, - SCORPION_BANK_CD_ISIDE_HIT = 0x98, - SCORPION_BANK_CD_ISIDE_ACCESS = 0x99, - SCORPION_ISIDE_RD_WAIT = 0x9a, - SCORPION_DSIDE_RD_WAIT = 0x9b, - SCORPION_BANK_BYPASS_WRITE = 0x9c, - SCORPION_BANK_AB_NON_CASTOUT = 0x9d, - SCORPION_BANK_AB_L2_CASTOUT = 0x9e, - SCORPION_BANK_CD_NON_CASTOUT = 0x9f, - SCORPION_BANK_CD_L2_CASTOUT = 0xa0, -#endif - MSM_MAX_EVT -}; - -struct scorp_evt { - u32 evt_type; - u32 val; - u8 grp; - u32 evt_type_act; -}; - -static const struct scorp_evt sc_evt[] = { - {SCORPION_ICACHE_EXPL_INV, 0x80000500, 0, 0x4d}, - {SCORPION_ICACHE_MISS, 0x80050000, 0, 0x4e}, - {SCORPION_ICACHE_ACCESS, 0x85000000, 0, 0x4f}, - {SCORPION_ICACHE_CACHEREQ_L2, 0x86000000, 0, 0x4f}, - {SCORPION_ICACHE_NOCACHE_L2, 0x87000000, 0, 0x4f}, - {SCORPION_HIQUP_NOPED, 0x80080000, 0, 0x4e}, - {SCORPION_DATA_ABORT, 0x8000000a, 0, 0x4c}, - {SCORPION_IRQ, 0x80000a00, 0, 0x4d}, - {SCORPION_FIQ, 0x800a0000, 0, 0x4e}, - {SCORPION_ALL_EXCPT, 0x8a000000, 0, 0x4f}, - {SCORPION_UNDEF, 0x8000000b, 0, 0x4c}, - {SCORPION_SVC, 0x80000b00, 0, 0x4d}, - {SCORPION_SMC, 0x800b0000, 0, 0x4e}, - {SCORPION_PREFETCH_ABORT, 0x8b000000, 0, 0x4f}, - {SCORPION_INDEX_CHECK, 0x8000000c, 0, 0x4c}, - {SCORPION_NULL_CHECK, 0x80000c00, 0, 0x4d}, - {SCORPION_EXPL_ICIALLU, 0x8000000d, 0, 0x4c}, - {SCORPION_IMPL_ICIALLU, 0x80000d00, 0, 0x4d}, - {SCORPION_NONICIALLU_BTAC_INV, 0x800d0000, 0, 0x4e}, - {SCORPION_ICIMVAU_IMPL_ICIALLU, 0x8d000000, 0, 0x4f}, - - {SCORPION_SPIPE_ONLY_CYCLES, 0x80000600, 1, 0x51}, - {SCORPION_XPIPE_ONLY_CYCLES, 0x80060000, 1, 0x52}, - {SCORPION_DUAL_CYCLES, 0x86000000, 1, 0x53}, - {SCORPION_DISPATCH_ANY_CYCLES, 0x89000000, 1, 0x53}, - {SCORPION_FIFO_FULLBLK_CMT, 0x8000000d, 1, 0x50}, - {SCORPION_FAIL_COND_INST, 0x800d0000, 1, 0x52}, - {SCORPION_PASS_COND_INST, 0x8d000000, 1, 0x53}, - {SCORPION_ALLOW_VU_CLK, 0x8000000e, 1, 0x50}, - {SCORPION_VU_IDLE, 0x80000e00, 1, 0x51}, - {SCORPION_ALLOW_L2_CLK, 0x800e0000, 1, 0x52}, - {SCORPION_L2_IDLE, 0x8e000000, 1, 0x53}, - - {SCORPION_DTLB_IMPL_INV_SCTLR_DACR, 0x80000001, 2, 0x54}, - {SCORPION_DTLB_EXPL_INV, 0x80000100, 2, 0x55}, - {SCORPION_DTLB_MISS, 0x80010000, 2, 0x56}, - {SCORPION_DTLB_ACCESS, 0x81000000, 2, 0x57}, - {SCORPION_ITLB_MISS, 0x80000200, 2, 0x55}, - {SCORPION_ITLB_IMPL_INV, 0x80020000, 2, 0x56}, - {SCORPION_ITLB_EXPL_INV, 0x82000000, 2, 0x57}, - {SCORPION_UTLB_D_MISS, 0x80000003, 2, 0x54}, - {SCORPION_UTLB_D_ACCESS, 0x80000300, 2, 0x55}, - {SCORPION_UTLB_I_MISS, 0x80030000, 2, 0x56}, - {SCORPION_UTLB_I_ACCESS, 0x83000000, 2, 0x57}, - {SCORPION_UTLB_INV_ASID, 0x80000400, 2, 0x55}, - {SCORPION_UTLB_INV_MVA, 0x80040000, 2, 0x56}, - {SCORPION_UTLB_INV_ALL, 0x84000000, 2, 0x57}, - {SCORPION_S2_HOLD_RDQ_UNAVAIL, 0x80000800, 2, 0x55}, - {SCORPION_S2_HOLD, 0x88000000, 2, 0x57}, - {SCORPION_S2_HOLD_DEV_OP, 0x80000900, 2, 0x55}, - {SCORPION_S2_HOLD_ORDER, 0x80090000, 2, 0x56}, - {SCORPION_S2_HOLD_BARRIER, 0x89000000, 2, 0x57}, - - {SCORPION_VIU_DUAL_CYCLE, 0x80000001, 4, 0x5c}, - {SCORPION_VIU_SINGLE_CYCLE, 0x80000100, 4, 0x5d}, - {SCORPION_VX_PIPE_WAR_STALL_CYCLES, 0x80000005, 4, 0x5c}, - {SCORPION_VX_PIPE_WAW_STALL_CYCLES, 0x80000500, 4, 0x5d}, - {SCORPION_VX_PIPE_RAW_STALL_CYCLES, 0x80050000, 4, 0x5e}, - {SCORPION_VX_PIPE_LOAD_USE_STALL, 0x80000007, 4, 0x5c}, - {SCORPION_VS_PIPE_WAR_STALL_CYCLES, 0x80000008, 4, 0x5c}, - {SCORPION_VS_PIPE_WAW_STALL_CYCLES, 0x80000800, 4, 0x5d}, - {SCORPION_VS_PIPE_RAW_STALL_CYCLES, 0x80080000, 4, 0x5e}, - {SCORPION_EXCEPTIONS_INV_OPERATION, 0x8000000b, 4, 0x5c}, - {SCORPION_EXCEPTIONS_DIV_BY_ZERO, 0x80000b00, 4, 0x5d}, - {SCORPION_COND_INST_FAIL_VX_PIPE, 0x800b0000, 4, 0x5e}, - {SCORPION_COND_INST_FAIL_VS_PIPE, 0x8b000000, 4, 0x5f}, - {SCORPION_EXCEPTIONS_OVERFLOW, 0x8000000c, 4, 0x5c}, - {SCORPION_EXCEPTIONS_UNDERFLOW, 0x80000c00, 4, 0x5d}, - {SCORPION_EXCEPTIONS_DENORM, 0x8c000000, 4, 0x5f}, - -#ifdef CONFIG_ARCH_MSM_SCORPIONMP - {SCORPIONMP_NUM_BARRIERS, 0x80000e00, 3, 0x59}, - {SCORPIONMP_BARRIER_CYCLES, 0x800e0000, 3, 0x5a}, -#else - {SCORPION_BANK_AB_HIT, 0x80000001, 3, 0x58}, - {SCORPION_BANK_AB_ACCESS, 0x80000100, 3, 0x59}, - {SCORPION_BANK_CD_HIT, 0x80010000, 3, 0x5a}, - {SCORPION_BANK_CD_ACCESS, 0x81000000, 3, 0x5b}, - {SCORPION_BANK_AB_DSIDE_HIT, 0x80000002, 3, 0x58}, - {SCORPION_BANK_AB_DSIDE_ACCESS, 0x80000200, 3, 0x59}, - {SCORPION_BANK_CD_DSIDE_HIT, 0x80020000, 3, 0x5a}, - {SCORPION_BANK_CD_DSIDE_ACCESS, 0x82000000, 3, 0x5b}, - {SCORPION_BANK_AB_ISIDE_HIT, 0x80000003, 3, 0x58}, - {SCORPION_BANK_AB_ISIDE_ACCESS, 0x80000300, 3, 0x59}, - {SCORPION_BANK_CD_ISIDE_HIT, 0x80030000, 3, 0x5a}, - {SCORPION_BANK_CD_ISIDE_ACCESS, 0x83000000, 3, 0x5b}, - {SCORPION_ISIDE_RD_WAIT, 0x80000009, 3, 0x58}, - {SCORPION_DSIDE_RD_WAIT, 0x80090000, 3, 0x5a}, - {SCORPION_BANK_BYPASS_WRITE, 0x8000000a, 3, 0x58}, - {SCORPION_BANK_AB_NON_CASTOUT, 0x8000000c, 3, 0x58}, - {SCORPION_BANK_AB_L2_CASTOUT, 0x80000c00, 3, 0x59}, - {SCORPION_BANK_CD_NON_CASTOUT, 0x800c0000, 3, 0x5a}, - {SCORPION_BANK_CD_L2_CASTOUT, 0x8c000000, 3, 0x5b}, -#endif -}; - -static inline void scorpion_pmnc_write(u32 val) -{ - val &= PMNC_MASK; - asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val)); -} - -static inline u32 scorpion_pmnc_read(void) -{ - u32 val; - - asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val)); - return val; -} - -static inline u32 scorpion_ccnt_read(void) -{ - u32 val; - - asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); - return val; -} - -static inline u32 scorpion_cntn_read(void) -{ - u32 val; - - asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val)); - return val; -} - -static inline u32 scorpion_pmnc_enable_counter(unsigned int cnt) -{ - u32 val; - - if (cnt >= CNTMAX) { - pr_err("gator: CPU%u enabling wrong PMNC counter %d\n", smp_processor_id(), cnt); - return -1; - } - - if (cnt == CCNT) - val = CCNT_REG; - else - val = (1 << (cnt - CNT0)); - - asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val)); - - return cnt; -} - -static inline u32 scorpion_pmnc_disable_counter(unsigned int cnt) -{ - u32 val; - - if (cnt >= CNTMAX) { - pr_err("gator: CPU%u disabling wrong PMNC counter %d\n", smp_processor_id(), cnt); - return -1; - } - - if (cnt == CCNT) - val = CCNT_REG; - else - val = (1 << (cnt - CNT0)); - - asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val)); - - return cnt; -} - -static inline int scorpion_pmnc_select_counter(unsigned int cnt) -{ - u32 val; - - if ((cnt == CCNT) || (cnt >= CNTMAX)) { - pr_err("gator: CPU%u selecting wrong PMNC counter %d\n", smp_processor_id(), cnt); - return -1; - } - - val = (cnt - CNT0); - asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val)); - - return cnt; -} - -static u32 scorpion_read_lpm0(void) -{ - u32 val; - - asm volatile("mrc p15, 0, %0, c15, c0, 0" : "=r" (val)); - return val; -} - -static void scorpion_write_lpm0(u32 val) -{ - asm volatile("mcr p15, 0, %0, c15, c0, 0" : : "r" (val)); -} - -static u32 scorpion_read_lpm1(void) -{ - u32 val; - - asm volatile("mrc p15, 1, %0, c15, c0, 0" : "=r" (val)); - return val; -} - -static void scorpion_write_lpm1(u32 val) -{ - asm volatile("mcr p15, 1, %0, c15, c0, 0" : : "r" (val)); -} - -static u32 scorpion_read_lpm2(void) -{ - u32 val; - - asm volatile("mrc p15, 2, %0, c15, c0, 0" : "=r" (val)); - return val; -} - -static void scorpion_write_lpm2(u32 val) -{ - asm volatile("mcr p15, 2, %0, c15, c0, 0" : : "r" (val)); -} - -static u32 scorpion_read_l2lpm(void) -{ - u32 val; - - asm volatile("mrc p15, 3, %0, c15, c2, 0" : "=r" (val)); - return val; -} - -static void scorpion_write_l2lpm(u32 val) -{ - asm volatile("mcr p15, 3, %0, c15, c2, 0" : : "r" (val)); -} - -static u32 scorpion_read_vlpm(void) -{ - u32 val; - - asm volatile("mrc p10, 7, %0, c11, c0, 0" : "=r" (val)); - return val; -} - -static void scorpion_write_vlpm(u32 val) -{ - asm volatile("mcr p10, 7, %0, c11, c0, 0" : : "r" (val)); -} - -struct scorpion_access_funcs { - u32 (*read)(void); - void (*write)(u32); -}; - -struct scorpion_access_funcs scor_func[] = { - {scorpion_read_lpm0, scorpion_write_lpm0}, - {scorpion_read_lpm1, scorpion_write_lpm1}, - {scorpion_read_lpm2, scorpion_write_lpm2}, - {scorpion_read_l2lpm, scorpion_write_l2lpm}, - {scorpion_read_vlpm, scorpion_write_vlpm}, -}; - -u32 venum_orig_val; -u32 fp_orig_val; - -static void scorpion_pre_vlpm(void) -{ - u32 venum_new_val; - u32 fp_new_val; - - /* CPACR Enable CP10 access */ - asm volatile("mrc p15, 0, %0, c1, c0, 2" : "=r" (venum_orig_val)); - venum_new_val = venum_orig_val | 0x00300000; - asm volatile("mcr p15, 0, %0, c1, c0, 2" : : "r" (venum_new_val)); - /* Enable FPEXC */ - asm volatile("mrc p10, 7, %0, c8, c0, 0" : "=r" (fp_orig_val)); - fp_new_val = fp_orig_val | 0x40000000; - asm volatile("mcr p10, 7, %0, c8, c0, 0" : : "r" (fp_new_val)); -} - -static void scorpion_post_vlpm(void) -{ - /* Restore FPEXC */ - asm volatile("mcr p10, 7, %0, c8, c0, 0" : : "r" (fp_orig_val)); - /* Restore CPACR */ - asm volatile("mcr p15, 0, %0, c1, c0, 2" : : "r" (venum_orig_val)); -} - -#define COLMN0MASK 0x000000ff -#define COLMN1MASK 0x0000ff00 -#define COLMN2MASK 0x00ff0000 -static u32 scorpion_get_columnmask(u32 setval) -{ - if (setval & COLMN0MASK) - return 0xffffff00; - if (setval & COLMN1MASK) - return 0xffff00ff; - if (setval & COLMN2MASK) - return 0xff00ffff; - return 0x80ffffff; -} - -static void scorpion_evt_setup(u32 gr, u32 setval) -{ - u32 val; - - if (gr == 4) - scorpion_pre_vlpm(); - val = scorpion_get_columnmask(setval) & scor_func[gr].read(); - val = val | setval; - scor_func[gr].write(val); - if (gr == 4) - scorpion_post_vlpm(); -} - -static int get_scorpion_evtinfo(unsigned int evt_type, struct scorp_evt *evtinfo) -{ - u32 idx; - - if ((evt_type < 0x4c) || (evt_type >= MSM_MAX_EVT)) - return 0; - idx = evt_type - 0x4c; - if (sc_evt[idx].evt_type == evt_type) { - evtinfo->val = sc_evt[idx].val; - evtinfo->grp = sc_evt[idx].grp; - evtinfo->evt_type_act = sc_evt[idx].evt_type_act; - return 1; - } - return 0; -} - -static inline void scorpion_pmnc_write_evtsel(unsigned int cnt, u32 val) -{ - if (scorpion_pmnc_select_counter(cnt) == cnt) { - if (val < 0x40) { - asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val)); - } else { - u32 zero = 0; - struct scorp_evt evtinfo; - /* extract evtinfo.grp and evtinfo.tevt_type_act from val */ - if (get_scorpion_evtinfo(val, &evtinfo) == 0) - return; - asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (evtinfo.evt_type_act)); - asm volatile("mcr p15, 0, %0, c9, c15, 0" : : "r" (zero)); - scorpion_evt_setup(evtinfo.grp, val); - } - } -} - -static void scorpion_pmnc_reset_counter(unsigned int cnt) -{ - u32 val = 0; - - if (cnt == CCNT) { - scorpion_pmnc_disable_counter(cnt); - - asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val)); - - if (pmnc_enabled[cnt] != 0) - scorpion_pmnc_enable_counter(cnt); - - } else if (cnt >= CNTMAX) { - pr_err("gator: CPU%u resetting wrong PMNC counter %d\n", smp_processor_id(), cnt); - } else { - scorpion_pmnc_disable_counter(cnt); - - if (scorpion_pmnc_select_counter(cnt) == cnt) - asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val)); - - if (pmnc_enabled[cnt] != 0) - scorpion_pmnc_enable_counter(cnt); - } -} - -static int gator_events_scorpion_create_files(struct super_block *sb, struct dentry *root) -{ - struct dentry *dir; - int i; - - for (i = 0; i < pmnc_counters; i++) { - char buf[40]; - - if (i == 0) - snprintf(buf, sizeof(buf), "%s_ccnt", pmnc_name); - else - snprintf(buf, sizeof(buf), "%s_cnt%d", pmnc_name, i - 1); - dir = gatorfs_mkdir(sb, root, buf); - if (!dir) - return -1; - gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); - gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); - if (i > 0) - gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); - } - - return 0; -} - -static int gator_events_scorpion_online(int **buffer, bool migrate) -{ - unsigned int cnt, len = 0, cpu = smp_processor_id(); - - if (scorpion_pmnc_read() & PMNC_E) - scorpion_pmnc_write(scorpion_pmnc_read() & ~PMNC_E); - - /* Initialize & Reset PMNC: C bit and P bit */ - scorpion_pmnc_write(PMNC_P | PMNC_C); - - for (cnt = CCNT; cnt < CNTMAX; cnt++) { - unsigned long event; - - if (!pmnc_enabled[cnt]) - continue; - - /* disable counter */ - scorpion_pmnc_disable_counter(cnt); - - event = pmnc_event[cnt] & 255; - - /* Set event (if destined for PMNx counters), We don't need to set the event if it's a cycle count */ - if (cnt != CCNT) - scorpion_pmnc_write_evtsel(cnt, event); - - /* reset counter */ - scorpion_pmnc_reset_counter(cnt); - - /* Enable counter, do not enable interrupt for this counter */ - scorpion_pmnc_enable_counter(cnt); - } - - /* enable */ - scorpion_pmnc_write(scorpion_pmnc_read() | PMNC_E); - - /* read the counters and toss the invalid data, return zero instead */ - for (cnt = 0; cnt < pmnc_counters; cnt++) { - if (pmnc_enabled[cnt]) { - if (cnt == CCNT) - scorpion_ccnt_read(); - else if (scorpion_pmnc_select_counter(cnt) == cnt) - scorpion_cntn_read(); - scorpion_pmnc_reset_counter(cnt); - - per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; - per_cpu(perfCnt, cpu)[len++] = 0; - } - } - - if (buffer) - *buffer = per_cpu(perfCnt, cpu); - - return len; -} - -static int gator_events_scorpion_offline(int **buffer, bool migrate) -{ - scorpion_pmnc_write(scorpion_pmnc_read() & ~PMNC_E); - return 0; -} - -static void gator_events_scorpion_stop(void) -{ - unsigned int cnt; - - for (cnt = CCNT; cnt < CNTMAX; cnt++) { - pmnc_enabled[cnt] = 0; - pmnc_event[cnt] = 0; - } -} - -static int gator_events_scorpion_read(int **buffer, bool sched_switch) -{ - int cnt, len = 0; - int cpu = smp_processor_id(); - - /* a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled */ - if (!(scorpion_pmnc_read() & PMNC_E)) - return 0; - - for (cnt = 0; cnt < pmnc_counters; cnt++) { - if (pmnc_enabled[cnt]) { - int value; - - if (cnt == CCNT) - value = scorpion_ccnt_read(); - else if (scorpion_pmnc_select_counter(cnt) == cnt) - value = scorpion_cntn_read(); - else - value = 0; - scorpion_pmnc_reset_counter(cnt); - - per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; - per_cpu(perfCnt, cpu)[len++] = value; - } - } - - if (buffer) - *buffer = per_cpu(perfCnt, cpu); - - return len; -} - -static struct gator_interface gator_events_scorpion_interface = { - .create_files = gator_events_scorpion_create_files, - .stop = gator_events_scorpion_stop, - .online = gator_events_scorpion_online, - .offline = gator_events_scorpion_offline, - .read = gator_events_scorpion_read, -}; - -int gator_events_scorpion_init(void) -{ - unsigned int cnt; - - switch (gator_cpuid()) { - case SCORPION: - pmnc_name = "Scorpion"; - pmnc_counters = 4; - break; - case SCORPIONMP: - pmnc_name = "ScorpionMP"; - pmnc_counters = 4; - break; - default: - return -1; - } - - /* CNT[n] + CCNT */ - pmnc_counters++; - - for (cnt = CCNT; cnt < CNTMAX; cnt++) { - pmnc_enabled[cnt] = 0; - pmnc_event[cnt] = 0; - pmnc_key[cnt] = gator_events_get_key(); - } - - return gator_events_install(&gator_events_scorpion_interface); -} - -#endif diff --git a/drivers/gator/gator_fs.c b/drivers/gator/gator_fs.c index d8fb357..509714a 100644 --- a/drivers/gator/gator_fs.c +++ b/drivers/gator/gator_fs.c @@ -18,16 +18,14 @@ #define gatorfs_MAGIC 0x24051020 #define TMPBUFSIZE 50 -DEFINE_SPINLOCK(gatorfs_lock); +static DEFINE_SPINLOCK(gatorfs_lock); static struct inode *gatorfs_get_inode(struct super_block *sb, int mode) { struct inode *inode = new_inode(sb); if (inode) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) inode->i_ino = get_next_ino(); -#endif inode->i_mode = mode; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; } @@ -312,16 +310,9 @@ static int gatorfs_fill_super(struct super_block *sb, void *data, int silent) root_inode->i_op = &simple_dir_inode_operations; root_inode->i_fop = &simple_dir_operations; -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) - root_dentry = d_alloc_root(root_inode); -#else root_dentry = d_make_root(root_inode); -#endif if (!root_dentry) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) - iput(root_inode); -#endif return -ENOMEM; } @@ -332,29 +323,16 @@ static int gatorfs_fill_super(struct super_block *sb, void *data, int silent) return 0; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) -static int gatorfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data, - struct vfsmount *mnt) -{ - return get_sb_single(fs_type, flags, data, gatorfs_fill_super, mnt); -} -#else static struct dentry *gatorfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return mount_nodev(fs_type, flags, data, gatorfs_fill_super); } -#endif static struct file_system_type gatorfs_type = { .owner = THIS_MODULE, .name = "gatorfs", -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) - .get_sb = gatorfs_get_sb, -#else .mount = gatorfs_mount, -#endif .kill_sb = kill_litter_super, }; diff --git a/drivers/gator/gator_hrtimer_gator.c b/drivers/gator/gator_hrtimer_gator.c index c1525e1..8b86ede 100644 --- a/drivers/gator/gator_hrtimer_gator.c +++ b/drivers/gator/gator_hrtimer_gator.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2014. All rights reserved. + * Copyright (C) ARM Limited 2011-2015. 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 @@ -7,10 +7,10 @@ * */ -void (*callback)(void); -DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer); -DEFINE_PER_CPU(ktime_t, hrtimer_expire); -DEFINE_PER_CPU(int, hrtimer_is_active); +static void (*callback)(void); +static DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer); +static DEFINE_PER_CPU(ktime_t, hrtimer_expire); +static DEFINE_PER_CPU(int, hrtimer_is_active); static ktime_t profiling_interval; static void gator_hrtimer_online(void); static void gator_hrtimer_offline(void); diff --git a/drivers/gator/gator_iks.c b/drivers/gator/gator_iks.c index fb78c10..e5a3b5b 100644 --- a/drivers/gator/gator_iks.c +++ b/drivers/gator/gator_iks.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -21,11 +21,9 @@ static int __lcpu_to_pcpu[NR_CPUS]; static const struct gator_cpu *gator_find_cpu_by_dt_name(const char *const name) { - int i; - - for (i = 0; gator_cpus[i].cpuid != 0; ++i) { - const struct gator_cpu *const gator_cpu = &gator_cpus[i]; + const struct gator_cpu *gator_cpu; + list_for_each_entry(gator_cpu, &gator_cpus, list) { if (gator_cpu->dt_name != NULL && strcmp(gator_cpu->dt_name, name) == 0) return gator_cpu; } diff --git a/drivers/gator/gator_main.c b/drivers/gator/gator_main.c index 30bf60d..d7066e8 100644 --- a/drivers/gator/gator_main.c +++ b/drivers/gator/gator_main.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -8,7 +8,7 @@ */ /* This version must match the gator daemon version */ -#define PROTOCOL_VERSION 20 +#define PROTOCOL_VERSION 231 static unsigned long gator_protocol_version = PROTOCOL_VERSION; #include @@ -24,13 +24,13 @@ static unsigned long gator_protocol_version = PROTOCOL_VERSION; #include #include #include -#include #include #include "gator.h" +#include "gator_src_md5.h" -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) -#error kernels prior to 2.6.32 are not supported +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) +#error Kernels prior to 3.4 not supported. DS-5 v5.21 and earlier supported 2.6.32 and later. #endif #if defined(MODULE) && !defined(CONFIG_MODULES) @@ -53,7 +53,7 @@ static unsigned long gator_protocol_version = PROTOCOL_VERSION; #error gator requires the kernel to have CONFIG_LOCAL_TIMERS defined on SMP systems #endif -#if (GATOR_PERF_SUPPORT) && (!(GATOR_PERF_PMU_SUPPORT)) +#if !(GATOR_PERF_PMU_SUPPORT) #ifndef CONFIG_PERF_EVENTS #error gator requires the kernel to have CONFIG_PERF_EVENTS defined to support pmu hardware counters #elif !defined CONFIG_HW_PERF_EVENTS @@ -92,21 +92,17 @@ static unsigned long gator_protocol_version = PROTOCOL_VERSION; /* Name Frame Messages */ #define MESSAGE_COOKIE 1 #define MESSAGE_THREAD_NAME 2 -#define MESSAGE_LINK 4 /* Scheduler Trace Frame Messages */ #define MESSAGE_SCHED_SWITCH 1 #define MESSAGE_SCHED_EXIT 2 -/* Idle Frame Messages */ -#define MESSAGE_IDLE_ENTER 1 -#define MESSAGE_IDLE_EXIT 2 - /* Summary Frame Messages */ #define MESSAGE_SUMMARY 1 #define MESSAGE_CORE_NAME 3 /* Activity Frame Messages */ +#define MESSAGE_LINK 1 #define MESSAGE_SWITCH 2 #define MESSAGE_EXIT 3 @@ -156,18 +152,14 @@ static unsigned long gator_response_type; static DEFINE_MUTEX(start_mutex); static DEFINE_MUTEX(gator_buffer_mutex); -bool event_based_sampling; +static bool event_based_sampling; static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait); static DECLARE_WAIT_QUEUE_HEAD(gator_annotate_wait); static struct timer_list gator_buffer_wake_up_timer; static bool gator_buffer_wake_run; /* Initialize semaphore unlocked to initialize memory values */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) -static DECLARE_MUTEX(gator_buffer_wake_sem); -#else static DEFINE_SEMAPHORE(gator_buffer_wake_sem); -#endif static struct task_struct *gator_buffer_wake_thread; static LIST_HEAD(gator_events); @@ -183,9 +175,11 @@ static DEFINE_PER_CPU(bool, in_scheduler_context); /****************************************************************************** * Prototypes ******************************************************************************/ -static u64 gator_get_time(void); static void gator_emit_perf_time(u64 time); static void gator_op_create_files(struct super_block *sb, struct dentry *root); +static void gator_backtrace_handler(struct pt_regs *const regs); +static int gator_events_perf_pmu_reread(void); +static int gator_events_perf_pmu_create_files(struct super_block *sb, struct dentry *root); /* gator_buffer is protected by being per_cpu and by having IRQs * disabled when writing to it. Most marshal_* calls take care of this @@ -222,8 +216,6 @@ static DEFINE_PER_CPU(u64, gator_buffer_commit_time); /* List of all gator events - new events must be added to this list */ #define GATOR_EVENTS_LIST \ - GATOR_EVENT(gator_events_armv6_init) \ - GATOR_EVENT(gator_events_armv7_init) \ GATOR_EVENT(gator_events_block_init) \ GATOR_EVENT(gator_events_ccn504_init) \ GATOR_EVENT(gator_events_irq_init) \ @@ -236,7 +228,6 @@ static DEFINE_PER_CPU(u64, gator_buffer_commit_time); GATOR_EVENT(gator_events_net_init) \ GATOR_EVENT(gator_events_perf_pmu_init) \ GATOR_EVENT(gator_events_sched_init) \ - GATOR_EVENT(gator_events_scorpion_init) \ #define GATOR_EVENT(EVENT_INIT) __weak int EVENT_INIT(void); GATOR_EVENTS_LIST @@ -252,6 +243,7 @@ GATOR_EVENTS_LIST * Application Includes ******************************************************************************/ #include "gator_fs.c" +#include "gator_pmu.c" #include "gator_buffer_write.c" #include "gator_buffer.c" #include "gator_marshaling.c" @@ -262,177 +254,14 @@ GATOR_EVENTS_LIST #include "gator_trace_power.c" #include "gator_trace_gpu.c" #include "gator_backtrace.c" +#include "gator_events_perf_pmu.c" /****************************************************************************** * Misc ******************************************************************************/ -static const struct gator_cpu gator_cpus[] = { - { - .cpuid = ARM1136, - .core_name = "ARM1136", - .pmnc_name = "ARM_ARM11", - .dt_name = "arm,arm1136", - .pmnc_counters = 3, - }, - { - .cpuid = ARM1156, - .core_name = "ARM1156", - .pmnc_name = "ARM_ARM11", - .dt_name = "arm,arm1156", - .pmnc_counters = 3, - }, - { - .cpuid = ARM1176, - .core_name = "ARM1176", - .pmnc_name = "ARM_ARM11", - .dt_name = "arm,arm1176", - .pmnc_counters = 3, - }, - { - .cpuid = ARM11MPCORE, - .core_name = "ARM11MPCore", - .pmnc_name = "ARM_ARM11MPCore", - .dt_name = "arm,arm11mpcore", - .pmnc_counters = 3, - }, - { - .cpuid = CORTEX_A5, - .core_name = "Cortex-A5", - .pmnc_name = "ARMv7_Cortex_A5", - .dt_name = "arm,cortex-a5", - .pmnc_counters = 2, - }, - { - .cpuid = CORTEX_A7, - .core_name = "Cortex-A7", - .pmnc_name = "ARMv7_Cortex_A7", - .dt_name = "arm,cortex-a7", - .pmnc_counters = 4, - }, - { - .cpuid = CORTEX_A8, - .core_name = "Cortex-A8", - .pmnc_name = "ARMv7_Cortex_A8", - .dt_name = "arm,cortex-a8", - .pmnc_counters = 4, - }, - { - .cpuid = CORTEX_A9, - .core_name = "Cortex-A9", - .pmnc_name = "ARMv7_Cortex_A9", - .dt_name = "arm,cortex-a9", - .pmnc_counters = 6, - }, - { - .cpuid = CORTEX_A15, - .core_name = "Cortex-A15", - .pmnc_name = "ARMv7_Cortex_A15", - .dt_name = "arm,cortex-a15", - .pmnc_counters = 6, - }, - { - .cpuid = CORTEX_A17, - .core_name = "Cortex-A17", - .pmnc_name = "ARMv7_Cortex_A17", - .dt_name = "arm,cortex-a17", - .pmnc_counters = 6, - }, - { - .cpuid = SCORPION, - .core_name = "Scorpion", - .pmnc_name = "Scorpion", - .pmnc_counters = 4, - }, - { - .cpuid = SCORPIONMP, - .core_name = "ScorpionMP", - .pmnc_name = "ScorpionMP", - .pmnc_counters = 4, - }, - { - .cpuid = KRAITSIM, - .core_name = "KraitSIM", - .pmnc_name = "Krait", - .pmnc_counters = 4, - }, - { - .cpuid = KRAIT, - .core_name = "Krait", - .pmnc_name = "Krait", - .pmnc_counters = 4, - }, - { - .cpuid = KRAIT_S4_PRO, - .core_name = "Krait S4 Pro", - .pmnc_name = "Krait", - .pmnc_counters = 4, - }, - { - .cpuid = CORTEX_A53, - .core_name = "Cortex-A53", - .pmnc_name = "ARM_Cortex-A53", - .dt_name = "arm,cortex-a53", - .pmnc_counters = 6, - }, - { - .cpuid = CORTEX_A57, - .core_name = "Cortex-A57", - .pmnc_name = "ARM_Cortex-A57", - .dt_name = "arm,cortex-a57", - .pmnc_counters = 6, - }, - { - .cpuid = AARCH64, - .core_name = "AArch64", - .pmnc_name = "ARM_AArch64", - .pmnc_counters = 6, - }, - { - .cpuid = OTHER, - .core_name = "Other", - .pmnc_name = "Other", - .pmnc_counters = 6, - }, - {} -}; - -const struct gator_cpu *gator_find_cpu_by_cpuid(const u32 cpuid) -{ - int i; - - for (i = 0; gator_cpus[i].cpuid != 0; ++i) { - const struct gator_cpu *const gator_cpu = &gator_cpus[i]; - - if (gator_cpu->cpuid == cpuid) - return gator_cpu; - } - - return NULL; -} - -static const char OLD_PMU_PREFIX[] = "ARMv7 Cortex-"; -static const char NEW_PMU_PREFIX[] = "ARMv7_Cortex_"; - -const struct gator_cpu *gator_find_cpu_by_pmu_name(const char *const name) -{ - int i; - - for (i = 0; gator_cpus[i].cpuid != 0; ++i) { - const struct gator_cpu *const gator_cpu = &gator_cpus[i]; - - if (gator_cpu->pmnc_name != NULL && - /* Do the names match exactly? */ - (strcasecmp(gator_cpu->pmnc_name, name) == 0 || - /* Do these names match but have the old vs new prefix? */ - ((strncasecmp(name, OLD_PMU_PREFIX, sizeof(OLD_PMU_PREFIX) - 1) == 0 && - strncasecmp(gator_cpu->pmnc_name, NEW_PMU_PREFIX, sizeof(NEW_PMU_PREFIX) - 1) == 0 && - strcasecmp(name + sizeof(OLD_PMU_PREFIX) - 1, gator_cpu->pmnc_name + sizeof(NEW_PMU_PREFIX) - 1) == 0)))) - return gator_cpu; - } - - return NULL; -} +MODULE_PARM_DESC(gator_src_md5, "Gator driver source code md5sum"); +module_param_named(src_md5, gator_src_md5, charp, 0444); u32 gator_cpuid(void) { @@ -443,7 +272,7 @@ u32 gator_cpuid(void) #else asm volatile("mrs %0, midr_el1" : "=r" (val)); #endif - return (val >> 4) & 0xfff; + return ((val & 0xff000000) >> 12) | ((val & 0xfff0) >> 4); #else return OTHER; #endif @@ -503,7 +332,7 @@ static void gator_timer_interrupt(void) gator_backtrace_handler(regs); } -void gator_backtrace_handler(struct pt_regs *const regs) +static void gator_backtrace_handler(struct pt_regs *const regs) { u64 time = gator_get_time(); int cpu = get_physical_cpu(); @@ -543,6 +372,8 @@ static void gator_timer_offline(void *migrate) list_for_each_entry(gi, &gator_events, list) { if (gi->offline) { len = gi->offline(&buffer, migrate); + if (len < 0) + pr_err("gator: offline failed for %s\n", gi->name); marshal_event(len, buffer); } } @@ -597,7 +428,7 @@ static void gator_send_core_name(const int cpu, const u32 cpuid) if (cpuid == -1) snprintf(core_name_buf, sizeof(core_name_buf), "Unknown"); else - snprintf(core_name_buf, sizeof(core_name_buf), "Unknown (0x%.3x)", cpuid); + snprintf(core_name_buf, sizeof(core_name_buf), "Unknown (0x%.5x)", cpuid); core_name = core_name_buf; } @@ -631,6 +462,8 @@ static void gator_timer_online(void *migrate) list_for_each_entry(gi, &gator_events, list) { if (gi->online) { len = gi->online(&buffer, migrate); + if (len < 0) + pr_err("gator: online failed for %s\n", gi->name); marshal_event(len, buffer); } } @@ -691,7 +524,7 @@ static int gator_timer_start(unsigned long sample_rate) return 0; } -static u64 gator_get_time(void) +u64 gator_get_time(void) { struct timespec ts; u64 timestamp; @@ -727,13 +560,12 @@ static u64 gator_get_time(void) static void gator_emit_perf_time(u64 time) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) if (time >= gator_sync_time) { - int cpu = get_physical_cpu(); - marshal_event_single64(0, -1, local_clock()); gator_sync_time += NSEC_PER_SEC; - gator_commit_buffer(cpu, COUNTER_BUF, time); + if (gator_live_rate <= 0) + gator_commit_buffer(get_physical_cpu(), COUNTER_BUF, time); } #endif } @@ -741,7 +573,7 @@ static void gator_emit_perf_time(u64 time) /****************************************************************************** * cpu hotplug and pm notifiers ******************************************************************************/ -static int __cpuinit gator_hotcpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) +static int gator_hotcpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { int cpu = lcpu_to_pcpu((long)hcpu); @@ -836,7 +668,7 @@ static void gator_summary(void) { u64 timestamp, uptime; struct timespec ts; - char uname_buf[512]; + char uname_buf[100]; snprintf(uname_buf, sizeof(uname_buf), "%s %s %s %s %s GNU/Linux", utsname()->sysname, utsname()->nodename, utsname()->release, utsname()->version, utsname()->machine); @@ -867,7 +699,9 @@ static void gator_summary(void) marshal_summary(timestamp, uptime, gator_monotonic_started, uname_buf); gator_sync_time = 0; - gator_emit_perf_time(gator_monotonic_started); + gator_emit_perf_time(gator_monotonic_started); + /* Always flush COUNTER_BUF so that the initial perf_time is received before it's used */ + gator_commit_buffer(get_physical_cpu(), COUNTER_BUF, 0); preempt_enable(); } @@ -1356,6 +1190,7 @@ static void gator_op_create_files(struct super_block *sb, struct dentry *root) struct dentry *dir; struct gator_interface *gi; int cpu; + int err; /* reinitialize default values */ gator_cpu_cores = 0; @@ -1377,14 +1212,17 @@ static void gator_op_create_files(struct super_block *sb, struct dentry *root) gatorfs_create_ro_u64(sb, root, "started", &gator_monotonic_started); gatorfs_create_u64(sb, root, "live_rate", &gator_live_rate); - /* Annotate interface */ gator_annotate_create_files(sb, root); /* Linux Events */ dir = gatorfs_mkdir(sb, root, "events"); + gator_pmu_create_files(sb, root, dir); list_for_each_entry(gi, &gator_events, list) - if (gi->create_files) - gi->create_files(sb, dir); + if (gi->create_files) { + err = gi->create_files(sb, dir); + if (err != 0) + pr_err("gator: create_files failed for %s\n", gi->name); + } /* Sched Events */ sched_trace_create_files(sb, dir); @@ -1463,7 +1301,7 @@ static int __init gator_module_init(void) return -1; } - setup_timer(&gator_buffer_wake_up_timer, gator_buffer_wake_up, 0); + setup_deferrable_timer_on_stack(&gator_buffer_wake_up_timer, gator_buffer_wake_up, 0); /* Initialize the list of cpuids */ memset(gator_cpuids, -1, sizeof(gator_cpuids)); @@ -1478,6 +1316,7 @@ static void __exit gator_module_exit(void) tracepoint_synchronize_unregister(); gator_exit(); gatorfs_unregister(); + gator_pmu_exit(); } module_init(gator_module_init); diff --git a/drivers/gator/gator_marshaling.c b/drivers/gator/gator_marshaling.c index 0d11676..9b1a465 100644 --- a/drivers/gator/gator_marshaling.c +++ b/drivers/gator/gator_marshaling.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012-2014. All rights reserved. + * Copyright (C) ARM Limited 2012-2015. 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 @@ -27,6 +27,7 @@ static void marshal_summary(long long timestamp, long long uptime, long long mon { unsigned long flags; int cpu = 0; + char buf[32]; local_irq_save(flags); gator_buffer_write_packed_int(cpu, SUMMARY_BUF, MESSAGE_SUMMARY); @@ -36,6 +37,9 @@ static void marshal_summary(long long timestamp, long long uptime, long long mon gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, monotonic_delta); gator_buffer_write_string(cpu, SUMMARY_BUF, "uname"); gator_buffer_write_string(cpu, SUMMARY_BUF, uname); + gator_buffer_write_string(cpu, SUMMARY_BUF, "PAGESIZE"); + snprintf(buf, sizeof(buf), "%lu", PAGE_SIZE); + gator_buffer_write_string(cpu, SUMMARY_BUF, buf); #if GATOR_IKS_SUPPORT gator_buffer_write_string(cpu, SUMMARY_BUF, "iks"); gator_buffer_write_string(cpu, SUMMARY_BUF, ""); @@ -59,6 +63,10 @@ static void marshal_summary(long long timestamp, long long uptime, long long mon gator_buffer_write_string(cpu, SUMMARY_BUF, "unknown"); #endif #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + gator_buffer_write_string(cpu, SUMMARY_BUF, "nosync"); + gator_buffer_write_string(cpu, SUMMARY_BUF, ""); +#endif gator_buffer_write_string(cpu, SUMMARY_BUF, ""); /* Commit the buffer now so it can be one of the first frames read by Streamline */ local_irq_restore(flags); @@ -107,16 +115,16 @@ static void marshal_link(int cookie, int tgid, int pid) local_irq_save(flags); time = gator_get_time(); - if (buffer_check_space(cpu, NAME_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, NAME_BUF, MESSAGE_LINK); - gator_buffer_write_packed_int64(cpu, NAME_BUF, time); - gator_buffer_write_packed_int(cpu, NAME_BUF, cookie); - gator_buffer_write_packed_int(cpu, NAME_BUF, tgid); - gator_buffer_write_packed_int(cpu, NAME_BUF, pid); + if (buffer_check_space(cpu, ACTIVITY_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, MESSAGE_LINK); + gator_buffer_write_packed_int64(cpu, ACTIVITY_BUF, time); + gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, cookie); + gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, tgid); + gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, pid); } local_irq_restore(flags); /* Check and commit; commit is set to occur once buffer is 3/4 full */ - buffer_check(cpu, NAME_BUF, time); + buffer_check(cpu, ACTIVITY_BUF, time); } static bool marshal_backtrace_header(int exec_cookie, int tgid, int pid, u64 time) diff --git a/drivers/gator/gator_pmu.c b/drivers/gator/gator_pmu.c new file mode 100644 index 0000000..5fc9c73 --- /dev/null +++ b/drivers/gator/gator_pmu.c @@ -0,0 +1,242 @@ +struct gator_cpu { + struct list_head list; + unsigned long cpuid; + unsigned long pmnc_counters; + /* Human readable name */ + char core_name[MAXSIZE_CORE_NAME]; + /* gatorfs event and Perf PMU name */ + char pmnc_name[MAXSIZE_CORE_NAME]; + /* compatible from Documentation/devicetree/bindings/arm/cpus.txt */ + char dt_name[MAXSIZE_CORE_NAME]; +}; + +struct uncore_pmu { + struct list_head list; + unsigned long pmnc_counters; + unsigned long has_cycles_counter; + /* Perf PMU name */ + char pmnc_name[MAXSIZE_CORE_NAME]; + /* gatorfs event name */ + char core_name[MAXSIZE_CORE_NAME]; +}; + +static LIST_HEAD(uncore_pmus); +static LIST_HEAD(gator_cpus); +static DEFINE_MUTEX(pmu_mutex); + +static struct super_block *gator_sb; +static struct dentry *gator_events_dir; + +static const struct gator_cpu *gator_find_cpu_by_cpuid(const u32 cpuid) +{ + const struct gator_cpu *gator_cpu; + + list_for_each_entry(gator_cpu, &gator_cpus, list) { + if (gator_cpu->cpuid == cpuid) + return gator_cpu; + } + + return NULL; +} + +static const char OLD_PMU_PREFIX[] = "ARMv7 Cortex-"; +static const char NEW_PMU_PREFIX[] = "ARMv7_Cortex_"; + +__maybe_unused +static const struct gator_cpu *gator_find_cpu_by_pmu_name(const char *const name) +{ + const struct gator_cpu *gator_cpu; + + list_for_each_entry(gator_cpu, &gator_cpus, list) { + if (gator_cpu->pmnc_name != NULL && + /* Do the names match exactly? */ + (strcasecmp(gator_cpu->pmnc_name, name) == 0 || + /* Do these names match but have the old vs new prefix? */ + ((strncasecmp(name, OLD_PMU_PREFIX, sizeof(OLD_PMU_PREFIX) - 1) == 0 && + strncasecmp(gator_cpu->pmnc_name, NEW_PMU_PREFIX, sizeof(NEW_PMU_PREFIX) - 1) == 0 && + strcasecmp(name + sizeof(OLD_PMU_PREFIX) - 1, gator_cpu->pmnc_name + sizeof(NEW_PMU_PREFIX) - 1) == 0)))) + return gator_cpu; + } + + return NULL; +} + +__maybe_unused +static const struct uncore_pmu *gator_find_uncore_pmu(const char *const name) +{ + const struct uncore_pmu *uncore_pmu; + + list_for_each_entry(uncore_pmu, &uncore_pmus, list) { + if (uncore_pmu->pmnc_name != NULL && strcasecmp(uncore_pmu->pmnc_name, name) == 0) + return uncore_pmu; + } + + return NULL; +} + +static bool gator_pmu_initialized; + +static ssize_t gator_pmu_init_write(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + if (gator_pmu_initialized) + return -EINVAL; + gator_pmu_initialized = true; + if (gator_events_perf_pmu_reread() != 0 || + gator_events_perf_pmu_create_files(gator_sb, gator_events_dir) != 0) + return -EINVAL; + return count; +} + +static const struct file_operations gator_pmu_init_fops = { + .write = gator_pmu_init_write, +}; + +static ssize_t gator_pmu_str_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + char *const val = file->private_data; + + return simple_read_from_buffer(buf, count, offset, val, strlen(val)); +} + +static ssize_t gator_pmu_str_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + char *value = file->private_data; + + if (*offset) + return -EINVAL; + + if (count >= MAXSIZE_CORE_NAME) + return -EINVAL; + if (copy_from_user(value, buf, count)) + return -EFAULT; + value[count] = 0; + value = strstrip(value); + + return count; +} + +static const struct file_operations gator_pmu_str_fops = { + .read = gator_pmu_str_read_file, + .write = gator_pmu_str_write_file, + .open = default_open, +}; + +static int gator_pmu_create_str(struct super_block *sb, struct dentry *root, char const *name, char *const val) +{ + struct dentry *d = __gatorfs_create_file(sb, root, name, &gator_pmu_str_fops, 0644); + if (!d) + return -EFAULT; + + d->d_inode->i_private = val; + return 0; +} + +static ssize_t gator_pmu_export_write(struct file *file, char const __user *ubuf, size_t count, loff_t *offset) +{ + struct dentry *dir; + struct dentry *parent; + char buf[MAXSIZE_CORE_NAME]; + const char *str; + + if (*offset) + return -EINVAL; + + if (count >= sizeof(buf)) + return -EINVAL; + if (copy_from_user(&buf, ubuf, count)) + return -EFAULT; + buf[count] = 0; + str = strstrip(buf); + + parent = file->f_path.dentry->d_parent; + dir = gatorfs_mkdir(gator_sb, parent, buf); + if (!dir) + return -EINVAL; + + if (strcmp("pmu", parent->d_name.name) == 0) { + struct gator_cpu *gator_cpu; + + gator_cpu = kmalloc(sizeof(*gator_cpu), GFP_KERNEL); + if (gator_cpu == NULL) + return -ENOMEM; + memset(gator_cpu, 0, sizeof(*gator_cpu)); + + gatorfs_create_ulong(gator_sb, dir, "cpuid", &gator_cpu->cpuid); + gator_pmu_create_str(gator_sb, dir, "core_name", gator_cpu->core_name); + strcpy(gator_cpu->pmnc_name, str); + gator_pmu_create_str(gator_sb, dir, "dt_name", gator_cpu->dt_name); + gatorfs_create_ulong(gator_sb, dir, "pmnc_counters", &gator_cpu->pmnc_counters); + + mutex_lock(&pmu_mutex); + list_add_tail(&gator_cpu->list, &gator_cpus); /* mutex */ + mutex_unlock(&pmu_mutex); + } else { + struct uncore_pmu *uncore_pmu; + + uncore_pmu = kmalloc(sizeof(*uncore_pmu), GFP_KERNEL); + if (uncore_pmu == NULL) + return -ENOMEM; + memset(uncore_pmu, 0, sizeof(*uncore_pmu)); + + strcpy(uncore_pmu->pmnc_name, str); + gator_pmu_create_str(gator_sb, dir, "core_name", uncore_pmu->core_name); + gatorfs_create_ulong(gator_sb, dir, "pmnc_counters", &uncore_pmu->pmnc_counters); + gatorfs_create_ulong(gator_sb, dir, "has_cycles_counter", &uncore_pmu->has_cycles_counter); + + mutex_lock(&pmu_mutex); + list_add_tail(&uncore_pmu->list, &uncore_pmus); /* mutex */ + mutex_unlock(&pmu_mutex); + } + + return count; +} + +static const struct file_operations export_fops = { + .write = gator_pmu_export_write, +}; + +static int gator_pmu_create_files(struct super_block *sb, struct dentry *root, struct dentry *events) +{ + struct dentry *dir; + + gator_sb = sb; + gator_events_dir = events; + + gatorfs_create_file(sb, root, "pmu_init", &gator_pmu_init_fops); + + dir = gatorfs_mkdir(sb, root, "pmu"); + if (!dir) + return -1; + + gatorfs_create_file(sb, dir, "export", &export_fops); + + dir = gatorfs_mkdir(sb, root, "uncore_pmu"); + if (!dir) + return -1; + + gatorfs_create_file(sb, dir, "export", &export_fops); + + return 0; +} + +static void gator_pmu_exit(void) +{ + mutex_lock(&pmu_mutex); + { + struct gator_cpu *gator_cpu; + struct gator_cpu *next; + + list_for_each_entry_safe(gator_cpu, next, &gator_cpus, list) { + kfree(gator_cpu); + } + } + { + struct uncore_pmu *uncore_pmu; + struct uncore_pmu *next; + + list_for_each_entry_safe(uncore_pmu, next, &uncore_pmus, list) { + kfree(uncore_pmu); + } + } + mutex_unlock(&pmu_mutex); +} diff --git a/drivers/gator/gator_trace_gpu.c b/drivers/gator/gator_trace_gpu.c index 5de9152..b35ccd5 100644 --- a/drivers/gator/gator_trace_gpu.c +++ b/drivers/gator/gator_trace_gpu.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -132,7 +132,7 @@ static void mali_activity_stop(int core, int key) gator_marshal_activity_switch(core, key, last_activity, last_pid); } -void mali_activity_clear(struct mali_counter mali_activity[], size_t mali_activity_size) +static void mali_activity_clear(struct mali_counter mali_activity[], size_t mali_activity_size) { int activity; int cores; diff --git a/drivers/gator/gator_trace_power.c b/drivers/gator/gator_trace_power.c index 46e04b2..7c7a051 100644 --- a/drivers/gator/gator_trace_power.c +++ b/drivers/gator/gator_trace_power.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2014. All rights reserved. + * Copyright (C) ARM Limited 2011-2015. 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 @@ -79,15 +79,8 @@ GATOR_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu)) if (state == per_cpu(idle_prev_state, cpu)) return; - if (implements_wfi()) { - if (state == PWR_EVENT_EXIT) { - /* transition from wfi to non-wfi */ - marshal_idle(cpu, MESSAGE_IDLE_EXIT); - } else { - /* transition from non-wfi to wfi */ - marshal_idle(cpu, MESSAGE_IDLE_ENTER); - } - } + if (implements_wfi()) + marshal_idle(cpu, state); per_cpu(idle_prev_state, cpu) = state; } diff --git a/drivers/gator/gator_trace_sched.c b/drivers/gator/gator_trace_sched.c index 6d7cbd7..84f747b 100644 --- a/drivers/gator/gator_trace_sched.c +++ b/drivers/gator/gator_trace_sched.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -8,9 +8,7 @@ */ #include -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) #include -#endif #include "gator.h" @@ -27,30 +25,31 @@ enum { static DEFINE_PER_CPU(uint64_t *, taskname_keys); static DEFINE_PER_CPU(int, collecting); -/* this array is never read as the cpu wait charts are derived +/* this array is never read as the cpu charts are derived * counters the files are needed, nonetheless, to show that these * counters are available */ -static ulong cpu_wait_enabled[CPU_WAIT_TOTAL]; -static ulong sched_cpu_key[CPU_WAIT_TOTAL]; +static const char *sched_trace_event_names[] = { + "Linux_cpu_wait_contention", + "Linux_cpu_wait_io", + "Linux_cpu_system", + "Linux_cpu_user", +}; +static ulong sched_trace_enabled[ARRAY_SIZE(sched_trace_event_names)]; +static ulong sched_trace_keys[ARRAY_SIZE(sched_trace_event_names)]; static int sched_trace_create_files(struct super_block *sb, struct dentry *root) { struct dentry *dir; + int i; - /* CPU Wait - Contention */ - dir = gatorfs_mkdir(sb, root, "Linux_cpu_wait_contention"); - if (!dir) - return -1; - gatorfs_create_ulong(sb, dir, "enabled", &cpu_wait_enabled[STATE_CONTENTION]); - gatorfs_create_ro_ulong(sb, dir, "key", &sched_cpu_key[STATE_CONTENTION]); - - /* CPU Wait - I/O */ - dir = gatorfs_mkdir(sb, root, "Linux_cpu_wait_io"); - if (!dir) - return -1; - gatorfs_create_ulong(sb, dir, "enabled", &cpu_wait_enabled[STATE_WAIT_ON_IO]); - gatorfs_create_ro_ulong(sb, dir, "key", &sched_cpu_key[STATE_WAIT_ON_IO]); + for (i = 0; i < ARRAY_SIZE(sched_trace_event_names); ++i) { + dir = gatorfs_mkdir(sb, root, sched_trace_event_names[i]); + if (!dir) + return -1; + gatorfs_create_ulong(sb, dir, "enabled", &sched_trace_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &sched_trace_keys[i]); + } return 0; } @@ -104,13 +103,19 @@ static void collect_counters(u64 time, struct task_struct *task, bool sched_swit list_for_each_entry(gi, &gator_events, list) { if (gi->read) { len = gi->read(&buffer, sched_switch); + if (len < 0) + pr_err("gator: read failed for %s\n", gi->name); marshal_event(len, buffer); } else if (gi->read64) { - len = gi->read64(&buffer64); + len = gi->read64(&buffer64, sched_switch); + if (len < 0) + pr_err("gator: read64 failed for %s\n", gi->name); marshal_event64(len, buffer64); } if (gi->read_proc && task != NULL) { len = gi->read_proc(&buffer64, task); + if (len < 0) + pr_err("gator: read_proc failed for %s\n", gi->name); marshal_event64(len, buffer64); } } @@ -165,7 +170,6 @@ GATOR_DEFINE_PROBE(sched_process_fork, TP_PROTO(struct task_struct *parent, stru gator_trace_emit_link(child); } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) GATOR_DEFINE_PROBE(sched_process_exec, TP_PROTO(struct task_struct *p, pid_t old_pid, struct linux_binprm *bprm)) { gator_trace_emit_link(p); @@ -179,12 +183,11 @@ GATOR_DEFINE_PROBE(task_rename, TP_PROTO(struct task_struct *task, const char *c { emit_pid_name(comm, task); } -#endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) -GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next)) -#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next)) +#else +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next)) #endif { int state; @@ -204,9 +207,6 @@ GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_ collect_counters(gator_get_time(), prev, true); per_cpu(collecting, cpu) = 0; -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) - gator_trace_emit_link(next); -#endif marshal_sched_trace_switch(next->pid, state); per_cpu(in_scheduler_context, cpu) = false; @@ -228,12 +228,10 @@ static int register_scheduler_tracepoints(void) /* register tracepoints */ if (GATOR_REGISTER_TRACE(sched_process_fork)) goto fail_sched_process_fork; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) if (GATOR_REGISTER_TRACE(sched_process_exec)) goto fail_sched_process_exec; if (GATOR_REGISTER_TRACE(task_rename)) goto fail_task_rename; -#endif if (GATOR_REGISTER_TRACE(sched_switch)) goto fail_sched_switch; if (GATOR_REGISTER_TRACE(sched_process_free)) @@ -251,12 +249,10 @@ static int register_scheduler_tracepoints(void) fail_sched_process_free: GATOR_UNREGISTER_TRACE(sched_switch); fail_sched_switch: -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) GATOR_UNREGISTER_TRACE(task_rename); fail_task_rename: GATOR_UNREGISTER_TRACE(sched_process_exec); fail_sched_process_exec: -#endif GATOR_UNREGISTER_TRACE(sched_process_fork); fail_sched_process_fork: pr_err("gator: tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); @@ -267,10 +263,8 @@ fail_sched_process_fork: static void unregister_scheduler_tracepoints(void) { GATOR_UNREGISTER_TRACE(sched_process_fork); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) GATOR_UNREGISTER_TRACE(sched_process_exec); GATOR_UNREGISTER_TRACE(task_rename); -#endif GATOR_UNREGISTER_TRACE(sched_switch); GATOR_UNREGISTER_TRACE(sched_process_free); pr_debug("gator: unregistered tracepoints\n"); @@ -314,8 +308,8 @@ static void gator_trace_sched_init(void) { int i; - for (i = 0; i < CPU_WAIT_TOTAL; i++) { - cpu_wait_enabled[i] = 0; - sched_cpu_key[i] = gator_events_get_key(); + for (i = 0; i < ARRAY_SIZE(sched_trace_enabled); i++) { + sched_trace_enabled[i] = 0; + sched_trace_keys[i] = gator_events_get_key(); } } diff --git a/drivers/gator/mali/mali_kbase_gator_api.h b/drivers/gator/mali/mali_kbase_gator_api.h deleted file mode 100644 index 5ed0697..0000000 --- a/drivers/gator/mali/mali_kbase_gator_api.h +++ /dev/null @@ -1,219 +0,0 @@ -/** - * Copyright (C) ARM Limited 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. - * - */ - -#ifndef _KBASE_GATOR_API_H_ -#define _KBASE_GATOR_API_H_ - -/** - * @brief This file describes the API used by Gator to collect hardware counters data from a Mali device. - */ - -/* This define is used by the gator kernel module compile to select which DDK - * API calling convention to use. If not defined (legacy DDK) gator assumes - * version 1. The version to DDK release mapping is: - * Version 1 API: DDK versions r1px, r2px - * Version 2 API: DDK versions r3px, r4px - * Version 3 API: DDK version r5p0 and newer - * - * API Usage - * ========= - * - * 1] Call kbase_gator_hwcnt_init_names() to return the list of short counter - * names for the GPU present in this device. - * - * 2] Create a kbase_gator_hwcnt_info structure and set the counter enables for - * the counters you want enabled. The enables can all be set for simplicity in - * most use cases, but disabling some will let you minimize bandwidth impact. - * - * 3] Call kbase_gator_hwcnt_init() using the above structure, to create a - * counter context. On successful return the DDK will have populated the - * structure with a variety of useful information. - * - * 4] Call kbase_gator_hwcnt_dump_irq() to queue a non-blocking request for a - * counter dump. If this returns a non-zero value the request has been queued, - * otherwise the driver has been unable to do so (typically because of another - * user of the instrumentation exists concurrently). - * - * 5] Call kbase_gator_hwcnt_dump_complete() to test whether the previously - * requested dump has been succesful. If this returns non-zero the counter dump - * has resolved, but the value of *success must also be tested as the dump - * may have not been successful. If it returns zero the counter dump was - * abandoned due to the device being busy (typically because of another - * user of the instrumentation exists concurrently). - * - * 6] Process the counters stored in the buffer pointed to by ... - * - * kbase_gator_hwcnt_info->kernel_dump_buffer - * - * In pseudo code you can find all of the counters via this approach: - * - * - * hwcnt_info # pointer to kbase_gator_hwcnt_info structure - * hwcnt_name # pointer to name list - * - * u32 * hwcnt_data = (u32*)hwcnt_info->kernel_dump_buffer - * - * # Iterate over each 64-counter block in this GPU configuration - * for( i = 0; i < hwcnt_info->nr_hwc_blocks; i++) { - * hwc_type type = hwcnt_info->hwc_layout[i]; - * - * # Skip reserved type blocks - they contain no counters at all - * if( type == RESERVED_BLOCK ) { - * continue; - * } - * - * size_t name_offset = type * 64; - * size_t data_offset = i * 64; - * - * # Iterate over the names of the counters in this block type - * for( j = 0; j < 64; j++) { - * const char * name = hwcnt_name[name_offset+j]; - * - * # Skip empty name strings - there is no counter here - * if( name[0] == '\0' ) { - * continue; - * } - * - * u32 data = hwcnt_data[data_offset+j]; - * - * printk( "COUNTER: %s DATA: %u\n", name, data ); - * } - * } - * - * - * Note that in most implementations you typically want to either SUM or - * AVERAGE multiple instances of the same counter if, for example, you have - * multiple shader cores or multiple L2 caches. The most sensible view for - * analysis is to AVERAGE shader core counters, but SUM L2 cache and MMU - * counters. - * - * 7] Goto 4, repeating until you want to stop collecting counters. - * - * 8] Release the dump resources by calling kbase_gator_hwcnt_term(). - * - * 9] Release the name table resources by calling kbase_gator_hwcnt_term_names(). - * This function must only be called if init_names() returned a non-NULL value. - **/ - -#define MALI_DDK_GATOR_API_VERSION 3 - -#if !defined(MALI_TRUE) - #define MALI_TRUE ((uint32_t)1) -#endif - -#if !defined(MALI_FALSE) - #define MALI_FALSE ((uint32_t)0) -#endif - -enum hwc_type { - JM_BLOCK = 0, - TILER_BLOCK, - SHADER_BLOCK, - MMU_L2_BLOCK, - RESERVED_BLOCK -}; - -struct kbase_gator_hwcnt_info { - - /* Passed from Gator to kbase */ - - /* the bitmask of enabled hardware counters for each counter block */ - uint16_t bitmask[4]; - - /* Passed from kbase to Gator */ - - /* ptr to counter dump memory */ - void *kernel_dump_buffer; - - /* size of counter dump memory */ - uint32_t size; - - /* the ID of the Mali device */ - uint32_t gpu_id; - - /* the number of shader cores in the GPU */ - uint32_t nr_cores; - - /* the number of core groups */ - uint32_t nr_core_groups; - - /* the memory layout of the performance counters */ - enum hwc_type *hwc_layout; - - /* the total number of hardware couter blocks */ - uint32_t nr_hwc_blocks; -}; - -/** - * @brief Opaque block of Mali data which Gator needs to return to the API later. - */ -struct kbase_gator_hwcnt_handles; - -/** - * @brief Initialize the resources Gator needs for performance profiling. - * - * @param in_out_info A pointer to a structure containing the enabled counters passed from Gator and all the Mali - * specific information that will be returned to Gator. On entry Gator must have populated the - * 'bitmask' field with the counters it wishes to enable for each class of counter block. - * Each entry in the array corresponds to a single counter class based on the "hwc_type" - * enumeration, and each bit corresponds to an enable for 4 sequential counters (LSB enables - * the first 4 counters in the block, and so on). See the GPU counter array as returned by - * kbase_gator_hwcnt_get_names() for the index values of each counter for the curernt GPU. - * - * @return Pointer to an opaque handle block on success, NULL on error. - */ -extern struct kbase_gator_hwcnt_handles *kbase_gator_hwcnt_init(struct kbase_gator_hwcnt_info *in_out_info); - -/** - * @brief Free all resources once Gator has finished using performance counters. - * - * @param in_out_info A pointer to a structure containing the enabled counters passed from Gator and all the - * Mali specific information that will be returned to Gator. - * @param opaque_handles A wrapper structure for kbase structures. - */ -extern void kbase_gator_hwcnt_term(struct kbase_gator_hwcnt_info *in_out_info, struct kbase_gator_hwcnt_handles *opaque_handles); - -/** - * @brief Poll whether a counter dump is successful. - * - * @param opaque_handles A wrapper structure for kbase structures. - * @param[out] success Non-zero on success, zero on failure. - * - * @return Zero if the dump is still pending, non-zero if the dump has completed. Note that a - * completed dump may not have dumped succesfully, so the caller must test for both - * a completed and successful dump before processing counters. - */ -extern uint32_t kbase_gator_instr_hwcnt_dump_complete(struct kbase_gator_hwcnt_handles *opaque_handles, uint32_t * const success); - -/** - * @brief Request the generation of a new counter dump. - * - * @param opaque_handles A wrapper structure for kbase structures. - * - * @return Zero if the hardware device is busy and cannot handle the request, non-zero otherwise. - */ -extern uint32_t kbase_gator_instr_hwcnt_dump_irq(struct kbase_gator_hwcnt_handles *opaque_handles); - -/** - * @brief This function is used to fetch the names table based on the Mali device in use. - * - * @param[out] total_number_of_counters The total number of counters short names in the Mali devices' list. - * - * @return Pointer to an array of strings of length *total_number_of_counters. - */ -extern const char * const *kbase_gator_hwcnt_init_names(uint32_t *total_number_of_counters); - -/** - * @brief This function is used to terminate the use of the names table. - * - * This function must only be called if the initial call to kbase_gator_hwcnt_init_names returned a non-NULL value. - */ -extern void kbase_gator_hwcnt_term_names(void); - -#endif diff --git a/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h b/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h index 2bc0b03..a5d1651 100644 --- a/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h +++ b/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 diff --git a/drivers/gator/mali/mali_utgard_profiling_gator_api.h b/drivers/gator/mali/mali_utgard_profiling_gator_api.h index d646531..f550490 100644 --- a/drivers/gator/mali/mali_utgard_profiling_gator_api.h +++ b/drivers/gator/mali/mali_utgard_profiling_gator_api.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 diff --git a/drivers/gator/mali_midgard.mk b/drivers/gator/mali_midgard.mk index 1b784d5..101d0a8 100644 --- a/drivers/gator/mali_midgard.mk +++ b/drivers/gator/mali_midgard.mk @@ -10,30 +10,38 @@ EXTRA_CFLAGS += -DMALI_USE_UMP=1 \ -DMALI_NO_MALI=0 DDK_DIR ?= . -ifneq ($(wildcard $(DDK_DIR)/drivers/gpu/arm/t6xx),) -KBASE_DIR = $(DDK_DIR)/drivers/gpu/arm/t6xx/kbase -OSK_DIR = $(DDK_DIR)/drivers/gpu/arm/t6xx/kbase/osk -endif -ifneq ($(wildcard $(DDK_DIR)/drivers/gpu/arm/midgard),) -KBASE_DIR = $(DDK_DIR)/drivers/gpu/arm/midgard -OSK_DIR = $(DDK_DIR)/drivers/gpu/arm/midgard/osk -EXTRA_CFLAGS += -DMALI_DIR_MIDGARD=1 -endif +ifneq ($(wildcard $(DDK_DIR)/$(CONFIG_GATOR_MALI_MIDGARD_PATH)/mali_kbase_gator_api.h),) + # r5p0/Fluorine - ... + EXTRA_CFLAGS += -DMALI_SIMPLE_API=1 \ + -DMALI_DIR_MIDGARD=1 \ + -I$(DDK_DIR)/$(CONFIG_GATOR_MALI_MIDGARD_PATH) \ -ifneq ($(wildcard $(DDK_DIR)/drivers/gpu/arm/midgard/mali_kbase_gator_api.h),) -EXTRA_CFLAGS += -DMALI_SIMPLE_API=1 -endif +else + ifneq ($(wildcard $(DDK_DIR)/$(CONFIG_GATOR_MALI_MIDGARD_PATH)/kbase),) + # ? - r3p0 + KBASE_DIR = $(DDK_DIR)/$(CONFIG_GATOR_MALI_MIDGARD_PATH)/kbase + OSK_DIR = $(DDK_DIR)/$(CONFIG_GATOR_MALI_MIDGARD_PATH)/kbase/osk + else + ifneq ($(wildcard $(DDK_DIR)/$(CONFIG_GATOR_MALI_MIDGARD_PATH)),) + # r4p0/Europium - r4p1/Europium-Inc + KBASE_DIR = $(DDK_DIR)/$(CONFIG_GATOR_MALI_MIDGARD_PATH) + OSK_DIR = $(DDK_DIR)/$(CONFIG_GATOR_MALI_MIDGARD_PATH)/osk + EXTRA_CFLAGS += -DMALI_DIR_MIDGARD=1 + endif + endif -UMP_DIR = $(DDK_DIR)/include/linux + UMP_DIR = $(DDK_DIR)/include/linux -# Include directories in the DDK -EXTRA_CFLAGS += -I$(KBASE_DIR)/ \ - -I$(KBASE_DIR)/.. \ - -I$(OSK_DIR)/.. \ - -I$(UMP_DIR)/.. \ - -I$(DDK_DIR)/include \ - -I$(KBASE_DIR)/osk/src/linux/include \ - -I$(KBASE_DIR)/platform_dummy \ - -I$(KBASE_DIR)/src + # Include directories in the DDK + EXTRA_CFLAGS += -I$(KBASE_DIR)/ \ + -I$(KBASE_DIR)/.. \ + -I$(OSK_DIR)/.. \ + -I$(UMP_DIR)/.. \ + -I$(DDK_DIR)/include \ + -I$(KBASE_DIR)/osk/src/linux/include \ + -I$(KBASE_DIR)/platform_dummy \ + -I$(KBASE_DIR)/src \ + -Idrivers/staging/android \ +endif diff --git a/tools/gator/daemon/Android.mk b/tools/gator/daemon/Android.mk index 970ac69..dc7b5fc 100644 --- a/tools/gator/daemon/Android.mk +++ b/tools/gator/daemon/Android.mk @@ -1,13 +1,13 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h defaults_xml.h) +XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h defaults_xml.h SrcMd5.cpp) LOCAL_SRC_FILES := \ AnnotateListener.cpp \ + AtraceDriver.cpp \ Buffer.cpp \ CCNDriver.cpp \ - CPUFreqDriver.cpp \ CapturedXML.cpp \ Child.cpp \ Command.cpp \ @@ -17,11 +17,11 @@ LOCAL_SRC_FILES := \ DriverSource.cpp \ DynBuf.cpp \ EventsXML.cpp \ + ExternalDriver.cpp \ ExternalSource.cpp \ FSDriver.cpp \ Fifo.cpp \ FtraceDriver.cpp \ - FtraceSource.cpp \ HwmonDriver.cpp \ KMod.cpp \ LocalCapture.cpp \ @@ -29,6 +29,7 @@ LOCAL_SRC_FILES := \ main.cpp \ MaliVideoDriver.cpp \ MemInfoDriver.cpp\ + MidgardDriver.cpp \ Monitor.cpp \ NetDriver.cpp \ OlySocket.cpp \ @@ -37,13 +38,15 @@ LOCAL_SRC_FILES := \ PerfDriver.cpp \ PerfGroup.cpp \ PerfSource.cpp \ + PmuXML.cpp \ Proc.cpp \ Sender.cpp \ SessionData.cpp \ SessionXML.cpp \ - Setup.cpp \ Source.cpp \ + SrcMd5.cpp \ StreamlineSetup.cpp \ + TtraceDriver.cpp \ UEvent.cpp \ UserSpaceSource.cpp \ libsensors/access.c \ diff --git a/tools/gator/daemon/AnnotateListener.cpp b/tools/gator/daemon/AnnotateListener.cpp index 50110b4..5966cbe 100644 --- a/tools/gator/daemon/AnnotateListener.cpp +++ b/tools/gator/daemon/AnnotateListener.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2014. All rights reserved. + * Copyright (C) ARM Limited 2014-2015. 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 @@ -12,36 +12,56 @@ #include "OlySocket.h" +static const char STREAMLINE_ANNOTATE_PARENT[] = "\0streamline-annotate-parent"; + struct AnnotateClient { AnnotateClient *next; int fd; }; -AnnotateListener::AnnotateListener() : mClients(NULL), mSock(NULL) { +AnnotateListener::AnnotateListener() : mClients(NULL), mSock(NULL), mUds(NULL) { } AnnotateListener::~AnnotateListener() { close(); + delete mUds; delete mSock; } void AnnotateListener::setup() { mSock = new OlyServerSocket(8082); + mUds = new OlyServerSocket(STREAMLINE_ANNOTATE_PARENT, sizeof(STREAMLINE_ANNOTATE_PARENT), true); } -int AnnotateListener::getFd() { +int AnnotateListener::getSockFd() { return mSock->getFd(); } -void AnnotateListener::handle() { +void AnnotateListener::handleSock() { AnnotateClient *const client = new AnnotateClient(); client->fd = mSock->acceptConnection(); client->next = mClients; mClients = client; } +int AnnotateListener::getUdsFd() { + return mUds->getFd(); +} + +void AnnotateListener::handleUds() { + AnnotateClient *const client = new AnnotateClient(); + client->fd = mUds->acceptConnection(); + client->next = mClients; + mClients = client; +} + void AnnotateListener::close() { - mSock->closeServerSocket(); + if (mUds != NULL) { + mUds->closeServerSocket(); + } + if (mSock != NULL) { + mSock->closeServerSocket(); + } while (mClients != NULL) { ::close(mClients->fd); AnnotateClient *next = mClients->next; diff --git a/tools/gator/daemon/AnnotateListener.h b/tools/gator/daemon/AnnotateListener.h index cdefef1..6bc747d 100644 --- a/tools/gator/daemon/AnnotateListener.h +++ b/tools/gator/daemon/AnnotateListener.h @@ -1,12 +1,15 @@ /** - * Copyright (C) ARM Limited 2014. All rights reserved. + * Copyright (C) ARM Limited 2014-2015. 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. */ -class AnnotateClient; +#ifndef ANNOTATELISTENER_H +#define ANNOTATELISTENER_H + +struct AnnotateClient; class OlyServerSocket; class AnnotateListener { @@ -15,17 +18,22 @@ public: ~AnnotateListener(); void setup(); - int getFd(); + int getSockFd(); + int getUdsFd(); - void handle(); + void handleSock(); + void handleUds(); void close(); void signal(); private: AnnotateClient *mClients; OlyServerSocket *mSock; + OlyServerSocket *mUds; // Intentionally unimplemented AnnotateListener(const AnnotateListener &); AnnotateListener &operator=(const AnnotateListener &); }; + +#endif // ANNOTATELISTENER_H diff --git a/tools/gator/daemon/Application.mk b/tools/gator/daemon/Application.mk index 3ada471..8b0a788 100644 --- a/tools/gator/daemon/Application.mk +++ b/tools/gator/daemon/Application.mk @@ -1,3 +1,3 @@ -APP_PLATFORM := android-8 +APP_PLATFORM := android-9 # Replace armeabi-v7a with arm64-v8a to build an arm64 gatord or with armeabi to build an ARM11 gatord APP_ABI := armeabi-v7a diff --git a/tools/gator/daemon/AtraceDriver.cpp b/tools/gator/daemon/AtraceDriver.cpp new file mode 100644 index 0000000..2ee7aad --- /dev/null +++ b/tools/gator/daemon/AtraceDriver.cpp @@ -0,0 +1,127 @@ +/** + * Copyright (C) ARM Limited 2014-2015. 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 "AtraceDriver.h" + +#include + +#include "Logging.h" +#include "OlyUtility.h" +#include "SessionData.h" + +class AtraceCounter : public DriverCounter { +public: + AtraceCounter(DriverCounter *next, char *name, int flag); + ~AtraceCounter(); + + int getFlag() const { return mFlag; } + +private: + const int mFlag; + + // Intentionally unimplemented + AtraceCounter(const AtraceCounter &); + AtraceCounter &operator=(const AtraceCounter &); +}; + +AtraceCounter::AtraceCounter(DriverCounter *next, char *name, int flag) : DriverCounter(next, name), mFlag(flag) { +} + +AtraceCounter::~AtraceCounter() { +} + +AtraceDriver::AtraceDriver() : mSupported(false), mNotifyPath() { +} + +AtraceDriver::~AtraceDriver() { +} + +void AtraceDriver::readEvents(mxml_node_t *const xml) { + if (access("/system/bin/setprop", X_OK) != 0) { + // Reduce warning noise + //logg.logSetup("Atrace Disabled\nsetprop is not found, this is not an Android target"); + return; + } + if (!gSessionData.mFtraceDriver.isSupported()) { + logg.logSetup("Atrace Disabled\nftrace support is required"); + return; + } + + if (getApplicationFullPath(mNotifyPath, sizeof(mNotifyPath)) != 0) { + logg.logMessage("Unable to determine the full path of gatord, the cwd will be used"); + } + strncat(mNotifyPath, "notify.dex", sizeof(mNotifyPath) - strlen(mNotifyPath) - 1); + if (access(mNotifyPath, W_OK) != 0) { + logg.logSetup("Atrace Disabled\nunable to locate notify.dex"); + return; + } + + mSupported = true; + + mxml_node_t *node = xml; + while (true) { + node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND); + if (node == NULL) { + break; + } + const char *counter = mxmlElementGetAttr(node, "counter"); + if (counter == NULL) { + continue; + } + + if (strncmp(counter, "atrace_", 7) != 0) { + continue; + } + + const char *flag = mxmlElementGetAttr(node, "flag"); + if (flag == NULL) { + logg.logError("The atrace counter %s is missing the required flag attribute", counter); + handleException(); + } + setCounters(new AtraceCounter(getCounters(), strdup(counter), strtol(flag, NULL, 16))); + } +} + +void AtraceDriver::setAtrace(const int flags) { + logg.logMessage("Setting atrace flags to %i", flags); + pid_t pid = fork(); + if (pid < 0) { + logg.logError("fork failed"); + handleException(); + } else if (pid == 0) { + char buf[1<<10]; + snprintf(buf, sizeof(buf), "setprop debug.atrace.tags.enableflags %i; " + "dalvikvm -cp %s com.android.internal.util.WithFramework Notify", flags, mNotifyPath); + execlp("sh", "sh", "-c", buf, NULL); + exit(0); + } +} + +void AtraceDriver::start() { + if (!mSupported) { + return; + } + + int flags = 0; + for (AtraceCounter *counter = static_cast(getCounters()); counter != NULL; counter = static_cast(counter->getNext())) { + if (!counter->isEnabled()) { + continue; + } + flags |= counter->getFlag(); + } + + setAtrace(flags); +} + +void AtraceDriver::stop() { + if (!mSupported) { + return; + } + + setAtrace(0); +} diff --git a/tools/gator/daemon/AtraceDriver.h b/tools/gator/daemon/AtraceDriver.h new file mode 100644 index 0000000..0a06858 --- /dev/null +++ b/tools/gator/daemon/AtraceDriver.h @@ -0,0 +1,39 @@ +/** + * Copyright (C) ARM Limited 2015. 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. + */ + +#ifndef ATRACEDRIVER_H +#define ATRACEDRIVER_H + +#include "mxml/mxml.h" + +#include "Driver.h" + +class AtraceDriver : public SimpleDriver { +public: + AtraceDriver(); + ~AtraceDriver(); + + void readEvents(mxml_node_t *const xml); + + void start(); + void stop(); + + bool isSupported() const { return mSupported; } + +private: + void setAtrace(const int flags); + + bool mSupported; + char mNotifyPath[256]; + + // Intentionally unimplemented + AtraceDriver(const AtraceDriver &); + AtraceDriver &operator=(const AtraceDriver &); +}; + +#endif // ATRACEDRIVER_H diff --git a/tools/gator/daemon/Buffer.cpp b/tools/gator/daemon/Buffer.cpp index 8fa6280..de42232 100644 --- a/tools/gator/daemon/Buffer.cpp +++ b/tools/gator/daemon/Buffer.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -13,17 +13,21 @@ #include "SessionData.h" #define mask (mSize - 1) +#define FRAME_HEADER_SIZE 3 enum { - CODE_PEA = 1, - CODE_KEYS = 2, - CODE_FORMAT = 3, - CODE_MAPS = 4, - CODE_COMM = 5, - CODE_KEYS_OLD = 6, - CODE_ONLINE_CPU = 7, - CODE_OFFLINE_CPU = 8, - CODE_KALLSYMS = 9, + CODE_PEA = 1, + CODE_KEYS = 2, + CODE_FORMAT = 3, + CODE_MAPS = 4, + CODE_COMM = 5, + CODE_KEYS_OLD = 6, + CODE_ONLINE_CPU = 7, + CODE_OFFLINE_CPU = 8, + CODE_KALLSYMS = 9, + CODE_COUNTERS = 10, + CODE_HEADER_PAGE = 11, + CODE_HEADER_EVENT = 12, }; // Summary Frame Messages @@ -45,9 +49,9 @@ enum { /* 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) : mBuf(new char[size]), mReaderSem(readerSem), mCommitTime(gSessionData->mLiveRate), mSize(size), mReadPos(0), mWritePos(0), mCommitPos(0), mAvailable(true), mIsDone(false), mCore(core), mBufType(buftype) { +Buffer::Buffer(const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : mBuf(new char[size]), mReaderSem(readerSem), mCommitTime(gSessionData.mLiveRate), mSize(size), mReadPos(0), mWritePos(0), mCommitPos(0), mAvailable(true), mIsDone(false), mCore(core), mBufType(buftype) { if ((mSize & mask) != 0) { - logg->logError(__FILE__, __LINE__, "Buffer size is not a power of 2"); + logg.logError("Buffer size is not a power of 2"); handleException(); } sem_init(&mWriterSem, 0, 0); @@ -78,7 +82,7 @@ void Buffer::write(Sender *const sender) { length2 = commitPos; } - logg->logMessage("Sending data length1: %i length2: %i", length1, length2); + logg.logMessage("Sending data length1: %i length2: %i", length1, length2); // start, middle or end if (length1 > 0) { @@ -141,24 +145,38 @@ int Buffer::contiguousSpaceAvailable() const { } } -void Buffer::commit(const uint64_t time) { +bool Buffer::hasUncommittedMessages() const { + const int typeLength = gSessionData.mLocalCapture ? 0 : 1; + int length = mWritePos - mCommitPos; + if (length < 0) { + length += mSize; + } + length = length - typeLength - sizeof(int32_t); + return length > FRAME_HEADER_SIZE; +} + +void Buffer::commit(const uint64_t time, const bool force) { // 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; + const int typeLength = gSessionData.mLocalCapture ? 0 : 1; int length = mWritePos - mCommitPos; if (length < 0) { length += mSize; } length = length - typeLength - sizeof(int32_t); + if (!force && !mIsDone && length <= FRAME_HEADER_SIZE) { + // Nothing to write, only the frame header is present + return; + } 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); + logg.logMessage("Committing data mReadPos: %i mWritePos: %i mCommitPos: %i", mReadPos, mWritePos, mCommitPos); mCommitPos = mWritePos; - if (gSessionData->mLiveRate > 0) { + if (gSessionData.mLiveRate > 0) { while (time > mCommitTime) { - mCommitTime += gSessionData->mLiveRate; + mCommitTime += gSessionData.mLiveRate; } } @@ -175,7 +193,7 @@ void Buffer::check(const uint64_t time) { if (filled < 0) { filled += mSize; } - if (filled >= ((mSize * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= mCommitTime)) { + if (filled >= ((mSize * 3) / 4) || (gSessionData.mLiveRate > 0 && time >= mCommitTime)) { commit(time); } } @@ -246,7 +264,7 @@ void Buffer::writeString(const char *const str) { } void Buffer::frame() { - if (!gSessionData->mLocalCapture) { + if (!gSessionData.mLocalCapture) { packInt(RESPONSE_APC_DATA); } // Reserve space for the length @@ -257,7 +275,7 @@ void Buffer::frame() { } } -void Buffer::summary(const uint64_t currTime, const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname) { +void Buffer::summary(const uint64_t currTime, const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname, const long pageSize, const bool nosync) { packInt(MESSAGE_SUMMARY); writeString(NEWLINE_CANARY); packInt64(timestamp); @@ -265,6 +283,14 @@ void Buffer::summary(const uint64_t currTime, const int64_t timestamp, const int packInt64(monotonicDelta); writeString("uname"); writeString(uname); + writeString("PAGESIZE"); + char buf[32]; + snprintf(buf, sizeof(buf), "%li", pageSize); + writeString(buf); + if (nosync) { + writeString("nosync"); + writeString(""); + } writeString(""); check(currTime); } @@ -317,7 +343,7 @@ void Buffer::event64(const int key, const int64_t value) { } } -void Buffer::pea(const uint64_t currTime, const struct perf_event_attr *const pea, int key) { +void Buffer::marshalPea(const uint64_t currTime, const struct perf_event_attr *const pea, int key) { while (!checkSpace(2 * MAXSIZE_PACK32 + pea->size)) { sem_wait(&mWriterSem); } @@ -327,7 +353,7 @@ void Buffer::pea(const uint64_t currTime, const struct perf_event_attr *const pe check(currTime); } -void Buffer::keys(const uint64_t currTime, const int count, const __u64 *const ids, const int *const keys) { +void Buffer::marshalKeys(const uint64_t currTime, const int count, const __u64 *const ids, const int *const keys) { while (!checkSpace(2 * MAXSIZE_PACK32 + count * (MAXSIZE_PACK32 + MAXSIZE_PACK64))) { sem_wait(&mWriterSem); } @@ -340,7 +366,7 @@ void Buffer::keys(const uint64_t currTime, const int count, const __u64 *const i check(currTime); } -void Buffer::keysOld(const uint64_t currTime, const int keyCount, const int *const keys, const int bytes, const char *const buf) { +void Buffer::marshalKeysOld(const uint64_t currTime, const int keyCount, const int *const keys, const int bytes, const char *const buf) { while (!checkSpace((2 + keyCount) * MAXSIZE_PACK32 + bytes)) { sem_wait(&mWriterSem); } @@ -353,7 +379,7 @@ void Buffer::keysOld(const uint64_t currTime, const int keyCount, const int *con check(currTime); } -void Buffer::format(const uint64_t currTime, const int length, const char *const format) { +void Buffer::marshalFormat(const uint64_t currTime, const int length, const char *const format) { while (!checkSpace(MAXSIZE_PACK32 + length + 1)) { sem_wait(&mWriterSem); } @@ -362,7 +388,7 @@ void Buffer::format(const uint64_t currTime, const int length, const char *const check(currTime); } -void Buffer::maps(const uint64_t currTime, const int pid, const int tid, const char *const maps) { +void Buffer::marshalMaps(const uint64_t currTime, const int pid, const int tid, const char *const maps) { const int mapsLen = strlen(maps) + 1; while (!checkSpace(3 * MAXSIZE_PACK32 + mapsLen)) { sem_wait(&mWriterSem); @@ -374,7 +400,7 @@ void Buffer::maps(const uint64_t currTime, const int pid, const int tid, const c check(currTime); } -void Buffer::comm(const uint64_t currTime, const int pid, const int tid, const char *const image, const char *const comm) { +void Buffer::marshalComm(const uint64_t currTime, 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; while (!checkSpace(3 * MAXSIZE_PACK32 + imageLen + commLen)) { @@ -388,27 +414,27 @@ void Buffer::comm(const uint64_t currTime, const int pid, const int tid, const c check(currTime); } -void Buffer::onlineCPU(const uint64_t currTime, const uint64_t time, const int cpu) { +void Buffer::onlineCPU(const uint64_t currTime, const int cpu) { while (!checkSpace(MAXSIZE_PACK32 + MAXSIZE_PACK64)) { sem_wait(&mWriterSem); } packInt(CODE_ONLINE_CPU); - packInt64(time); + packInt64(currTime); packInt(cpu); check(currTime); } -void Buffer::offlineCPU(const uint64_t currTime, const uint64_t time, const int cpu) { +void Buffer::offlineCPU(const uint64_t currTime, const int cpu) { while (!checkSpace(MAXSIZE_PACK32 + MAXSIZE_PACK64)) { sem_wait(&mWriterSem); } packInt(CODE_OFFLINE_CPU); - packInt64(time); + packInt64(currTime); packInt(cpu); check(currTime); } -void Buffer::kallsyms(const uint64_t currTime, const char *const kallsyms) { +void Buffer::marshalKallsyms(const uint64_t currTime, const char *const kallsyms) { const int kallsymsLen = strlen(kallsyms) + 1; while (!checkSpace(3 * MAXSIZE_PACK32 + kallsymsLen)) { sem_wait(&mWriterSem); @@ -418,6 +444,51 @@ void Buffer::kallsyms(const uint64_t currTime, const char *const kallsyms) { check(currTime); } +void Buffer::perfCounterHeader(const uint64_t time) { + while (!checkSpace(MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + sem_wait(&mWriterSem); + } + packInt(CODE_COUNTERS); + packInt64(time); +} + +void Buffer::perfCounter(const int core, const int key, const int64_t value) { + while (!checkSpace(2*MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + sem_wait(&mWriterSem); + } + packInt(core); + packInt(key); + packInt64(value); +} + +void Buffer::perfCounterFooter(const uint64_t currTime) { + while (!checkSpace(MAXSIZE_PACK32)) { + sem_wait(&mWriterSem); + } + packInt(-1); + check(currTime); +} + +void Buffer::marshalHeaderPage(const uint64_t currTime, const char *const headerPage) { + const int headerPageLen = strlen(headerPage) + 1; + while (!checkSpace(MAXSIZE_PACK32 + headerPageLen)) { + sem_wait(&mWriterSem); + } + packInt(CODE_HEADER_PAGE); + writeBytes(headerPage, headerPageLen); + check(currTime); +} + +void Buffer::marshalHeaderEvent(const uint64_t currTime, const char *const headerEvent) { + const int headerEventLen = strlen(headerEvent) + 1; + while (!checkSpace(MAXSIZE_PACK32 + headerEventLen)) { + sem_wait(&mWriterSem); + } + packInt(CODE_HEADER_EVENT); + writeBytes(headerEvent, headerEventLen); + check(currTime); +} + void Buffer::setDone() { mIsDone = true; commit(0); diff --git a/tools/gator/daemon/Buffer.h b/tools/gator/daemon/Buffer.h index 6cffd8e..7dd8349 100644 --- a/tools/gator/daemon/Buffer.h +++ b/tools/gator/daemon/Buffer.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -36,11 +36,12 @@ public: int bytesAvailable() const; int contiguousSpaceAvailable() const; - void commit(const uint64_t time); + bool hasUncommittedMessages() const; + void commit(const uint64_t time, const bool force = false); void check(const uint64_t time); // Summary messages - void summary(const uint64_t currTime, const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname); + void summary(const uint64_t currTime, const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname, const long pageSize, const bool nosync); void coreName(const uint64_t currTime, const int core, const int cpuid, const char *const name); // Block Counter messages @@ -50,15 +51,20 @@ public: void event64(int key, int64_t value); // Perf Attrs messages - void pea(const uint64_t currTime, const struct perf_event_attr *const pea, int key); - void keys(const uint64_t currTime, const int count, const __u64 *const ids, const int *const keys); - void keysOld(const uint64_t currTime, const int keyCount, const int *const keys, const int bytes, const char *const buf); - void format(const uint64_t currTime, const int length, const char *const format); - void maps(const uint64_t currTime, const int pid, const int tid, const char *const maps); - void comm(const uint64_t currTime, const int pid, const int tid, const char *const image, const char *const comm); - void onlineCPU(const uint64_t currTime, const uint64_t time, const int cpu); - void offlineCPU(const uint64_t currTime, const uint64_t time, const int cpu); - void kallsyms(const uint64_t currTime, const char *const kallsyms); + void marshalPea(const uint64_t currTime, const struct perf_event_attr *const pea, int key); + void marshalKeys(const uint64_t currTime, const int count, const __u64 *const ids, const int *const keys); + void marshalKeysOld(const uint64_t currTime, const int keyCount, const int *const keys, const int bytes, const char *const buf); + void marshalFormat(const uint64_t currTime, const int length, const char *const format); + void marshalMaps(const uint64_t currTime, const int pid, const int tid, const char *const maps); + void marshalComm(const uint64_t currTime, const int pid, const int tid, const char *const image, const char *const comm); + void onlineCPU(const uint64_t currTime, const int cpu); + void offlineCPU(const uint64_t currTime, const int cpu); + void marshalKallsyms(const uint64_t currTime, const char *const kallsyms); + void perfCounterHeader(const uint64_t time); + void perfCounter(const int core, const int key, const int64_t value); + void perfCounterFooter(const uint64_t currTime); + void marshalHeaderPage(const uint64_t currTime, const char *const headerPage); + void marshalHeaderEvent(const uint64_t currTime, const char *const headerEvent); void setDone(); bool isDone() const; @@ -73,13 +79,24 @@ public: void writeBytes(const void *const data, size_t count); void writeString(const char *const str); - static void writeLEInt(unsigned char *buf, int v) { + static void writeLEInt(unsigned char *buf, uint32_t v) { buf[0] = (v >> 0) & 0xFF; buf[1] = (v >> 8) & 0xFF; buf[2] = (v >> 16) & 0xFF; buf[3] = (v >> 24) & 0xFF; } + static void writeLELong(unsigned char *buf, uint64_t v) { + buf[0] = (v >> 0) & 0xFF; + buf[1] = (v >> 8) & 0xFF; + buf[2] = (v >> 16) & 0xFF; + buf[3] = (v >> 24) & 0xFF; + buf[4] = (v >> 32) & 0xFF; + buf[5] = (v >> 40) & 0xFF; + buf[6] = (v >> 48) & 0xFF; + buf[7] = (v >> 56) & 0xFF; + } + private: void frame(); bool commitReady() const; diff --git a/tools/gator/daemon/CCNDriver.cpp b/tools/gator/daemon/CCNDriver.cpp index dd1a2b1..b881e81 100644 --- a/tools/gator/daemon/CCNDriver.cpp +++ b/tools/gator/daemon/CCNDriver.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2014. All rights reserved. + * Copyright (C) ARM Limited 2014-2015. 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 @@ -19,6 +19,7 @@ #include "Config.h" #include "DriverSource.h" #include "Logging.h" +#include "SessionData.h" static const char TAG_CATEGORY[] = "category"; static const char TAG_COUNTER_SET[] = "counter_set"; @@ -28,7 +29,6 @@ static const char TAG_OPTION_SET[] = "option_set"; static const char ATTR_AVERAGE_SELECTION[] = "average_selection"; static const char ATTR_COUNTER[] = "counter"; -static const char ATTR_COUNTER_SET[] = "counter_set"; static const char ATTR_COUNT[] = "count"; static const char ATTR_DESCRIPTION[] = "description"; static const char ATTR_DISPLAY[] = "display"; @@ -45,6 +45,8 @@ static const char RNI_REGION[] = "RN-I_Region"; static const char SBAS_REGION[] = "SBAS_Region"; static const char CCN_5XX[] = "CCN-5xx"; #define ARM_CCN_5XX "ARM_CCN_5XX_" +#define CCN_COUNT 8 +static const char ARM_CCN_5XX_CNT[] = ARM_CCN_5XX "cnt"; static const char *const VC_TYPES[] = { "REQ", "RSP", "SNP", "DAT" }; static const char *const XP_EVENT_NAMES[] = { NULL, "H-bit", "S-bit", "P-Cnt", "TknV" }; @@ -110,7 +112,7 @@ void CCNDriver::readEvents(mxml_node_t *const) { int type; if (DriverSource::readIntDriver("/sys/bus/event_source/devices/ccn/type", &type) != 0) { - logg->logError(__FILE__, __LINE__, "Unable to read CCN-5xx type"); + logg.logError("Unable to read CCN-5xx type"); handleException(); } @@ -174,12 +176,12 @@ int CCNDriver::writeCounters(mxml_node_t *const) const { void CCNDriver::writeEvents(mxml_node_t *const root) const { mxml_node_t *const counter_set = mxmlNewElement(root, TAG_COUNTER_SET); - mxmlElementSetAttr(counter_set, ATTR_NAME, ARM_CCN_5XX "cnt"); - mxmlElementSetAttr(counter_set, ATTR_COUNT, "8"); + mxmlElementSetAttr(counter_set, ATTR_NAME, ARM_CCN_5XX_CNT); + mxmlElementSetAttr(counter_set, ATTR_COUNT, STRIFY(CCN_COUNT)); mxml_node_t *const category = mxmlNewElement(root, TAG_CATEGORY); mxmlElementSetAttr(category, ATTR_NAME, CCN_5XX); - mxmlElementSetAttr(category, TAG_COUNTER_SET, ARM_CCN_5XX "cnt"); + mxmlElementSetAttr(category, TAG_COUNTER_SET, ARM_CCN_5XX_CNT); mxml_node_t *const clock_event = mxmlNewElement(category, TAG_EVENT); mxmlElementSetAttr(clock_event, ATTR_COUNTER, ARM_CCN_5XX "ccnt"); @@ -293,3 +295,35 @@ void CCNDriver::writeEvents(mxml_node_t *const root) const { } } } + +void CCNDriver::validateCounters() const { + int counts[CCN_COUNT][2] = { { 0 } }; + const unsigned int mask = getConfig(0xff, 0xff, 0, 0, 0); + + for (int i = 0; i < ARRAY_LENGTH(gSessionData.mCounters); ++i) { + const Counter *const counter = &gSessionData.mCounters[i]; + + if (!counter->isEnabled()) { + continue; + } + + if (strncmp(counter->getType(), ARM_CCN_5XX_CNT, sizeof(ARM_CCN_5XX_CNT) - 1) == 0) { + const int node = counter->getEvent() & mask; + + for (int j = 0; j < ARRAY_LENGTH(counts); ++j) { + if (counts[j][0] == 0) { + counts[j][0] = node; + } + if (counts[j][0] == node) { + ++counts[j][1]; + if (counts[j][1] > 4) { + if (asprintf(&gSessionData.mCountersError, "More than 4 events are assigned to the same CCN node") <= 0) { + logg.logError("asprintf failed"); + handleException(); + } + } + } + } + } + } +} diff --git a/tools/gator/daemon/CCNDriver.h b/tools/gator/daemon/CCNDriver.h index fb4c717..8a155a7 100644 --- a/tools/gator/daemon/CCNDriver.h +++ b/tools/gator/daemon/CCNDriver.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2014. All rights reserved. + * Copyright (C) ARM Limited 2014-2015. 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 @@ -24,6 +24,8 @@ public: int writeCounters(mxml_node_t *const root) const; void writeEvents(mxml_node_t *const) const; + void validateCounters() const; + private: enum NodeType { NT_UNKNOWN, diff --git a/tools/gator/daemon/COPYING b/tools/gator/daemon/COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/tools/gator/daemon/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/tools/gator/daemon/CPUFreqDriver.cpp b/tools/gator/daemon/CPUFreqDriver.cpp deleted file mode 100644 index 41f9d6f..0000000 --- a/tools/gator/daemon/CPUFreqDriver.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/** - * 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 "CPUFreqDriver.h" - -#include "Buffer.h" -#include "DriverSource.h" -#include "Logging.h" -#include "SessionData.h" - -CPUFreqDriver::CPUFreqDriver() : mPrev() { -} - -CPUFreqDriver::~CPUFreqDriver() { -} - -void CPUFreqDriver::readEvents(mxml_node_t *const) { - // Only for use with perf - if (!gSessionData->perf.isSetup()) { - return; - } - - setCounters(new DriverCounter(getCounters(), strdup("Linux_power_cpu_freq"))); -} - -void CPUFreqDriver::read(Buffer *const buffer) { - char buf[64]; - const DriverCounter *const counter = getCounters(); - if ((counter == NULL) || !counter->isEnabled()) { - return; - } - - const int key = getCounters()->getKey(); - bool resetCores = false; - for (int i = 0; i < gSessionData->mCores; ++i) { - snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpu%i/cpufreq/cpuinfo_cur_freq", i); - int64_t freq; - if (DriverSource::readInt64Driver(buf, &freq) != 0) { - freq = 0; - } - if (mPrev[i] != freq) { - mPrev[i] = freq; - // Change cores - buffer->event64(2, i); - resetCores = true; - buffer->event64(key, 1000*freq); - } - } - if (resetCores) { - // Revert cores, UserSpaceSource is all on core 0 - buffer->event64(2, 0); - } -} diff --git a/tools/gator/daemon/CPUFreqDriver.h b/tools/gator/daemon/CPUFreqDriver.h deleted file mode 100644 index ad8c9aa..0000000 --- a/tools/gator/daemon/CPUFreqDriver.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * 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. - */ - -#ifndef CPUFREQDRIVER_H -#define CPUFREQDRIVER_H - -#include "Config.h" -#include "Driver.h" - -class CPUFreqDriver : public PolledDriver { -private: - typedef PolledDriver super; - -public: - CPUFreqDriver(); - ~CPUFreqDriver(); - - void readEvents(mxml_node_t *const root); - void read(Buffer *const buffer); - -private: - int64_t mPrev[NR_CPUS]; - - // Intentionally unimplemented - CPUFreqDriver(const CPUFreqDriver &); - CPUFreqDriver &operator=(const CPUFreqDriver &); -}; - -#endif // CPUFREQDRIVER_H diff --git a/tools/gator/daemon/CapturedXML.cpp b/tools/gator/daemon/CapturedXML.cpp index 0b5802c..7a91a79 100644 --- a/tools/gator/daemon/CapturedXML.cpp +++ b/tools/gator/daemon/CapturedXML.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -32,9 +32,8 @@ mxml_node_t* CapturedXML::getTree(bool includeTime) { captured = mxmlNewElement(xml, "captured"); mxmlElementSetAttr(captured, "version", "1"); - if (gSessionData->perf.isSetup()) { + if (gSessionData.mPerf.isSetup()) { mxmlElementSetAttr(captured, "type", "Perf"); - mxmlElementSetAttr(captured, "perf_beta", "yes"); } mxmlElementSetAttrf(captured, "protocol", "%d", PROTOCOL_VERSION); if (includeTime) { // Send the following only after the capture is complete @@ -44,22 +43,22 @@ mxml_node_t* CapturedXML::getTree(bool includeTime) { } target = mxmlNewElement(captured, "target"); - mxmlElementSetAttr(target, "name", gSessionData->mCoreName); - mxmlElementSetAttrf(target, "sample_rate", "%d", gSessionData->mSampleRate); - mxmlElementSetAttrf(target, "cores", "%d", gSessionData->mCores); - mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mMaxCpuId); + mxmlElementSetAttr(target, "name", gSessionData.mCoreName); + mxmlElementSetAttrf(target, "sample_rate", "%d", gSessionData.mSampleRate); + mxmlElementSetAttrf(target, "cores", "%d", gSessionData.mCores); + mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData.mMaxCpuId); - if (!gSessionData->mOneShot && (gSessionData->mSampleRate > 0)) { + if (!gSessionData.mOneShot && (gSessionData.mSampleRate > 0)) { mxmlElementSetAttr(target, "supports_live", "yes"); } - if (gSessionData->mLocalCapture) { + if (gSessionData.mLocalCapture) { mxmlElementSetAttr(target, "local_capture", "yes"); } mxml_node_t *counters = NULL; for (x = 0; x < MAX_PERFORMANCE_COUNTERS; x++) { - const Counter & counter = gSessionData->mCounters[x]; + const Counter & counter = gSessionData.mCounters[x]; if (counter.isEnabled()) { if (counters == NULL) { counters = mxmlNewElement(captured, "counters"); @@ -97,8 +96,8 @@ void CapturedXML::write(char* path) { snprintf(file, PATH_MAX, "%s/captured.xml", path); char* xml = getXML(true); - if (util->writeToDisk(file, xml) < 0) { - logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file); + if (writeToDisk(file, xml) < 0) { + logg.logError("Error writing %s\nPlease verify the path.", file); handleException(); } diff --git a/tools/gator/daemon/CapturedXML.h b/tools/gator/daemon/CapturedXML.h index b704f6e..69d80c0 100644 --- a/tools/gator/daemon/CapturedXML.h +++ b/tools/gator/daemon/CapturedXML.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 diff --git a/tools/gator/daemon/Child.cpp b/tools/gator/daemon/Child.cpp index 6b5bbb3..df9fe23 100644 --- a/tools/gator/daemon/Child.cpp +++ b/tools/gator/daemon/Child.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -20,7 +20,6 @@ #include "Driver.h" #include "DriverSource.h" #include "ExternalSource.h" -#include "FtraceSource.h" #include "LocalCapture.h" #include "Logging.h" #include "OlySocket.h" @@ -35,7 +34,6 @@ static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shar static Source *primarySource = NULL; static Source *externalSource = NULL; static Source *userSpaceSource = NULL; -static Source *ftraceSource = NULL; static Sender* sender = NULL; // Shared by Child.cpp and spawned threads Child* child = NULL; // shared by Child.cpp and main.cpp @@ -43,18 +41,18 @@ extern void cleanUp(); void handleException() { if (child && child->numExceptions++ > 0) { // it is possible one of the below functions itself can cause an exception, thus allow only one exception - logg->logMessage("Received multiple exceptions, terminating the child"); - exit(1); + logg.logMessage("Received multiple exceptions, terminating the child"); + // Something is really wrong, exit immediately + _exit(1); } - fprintf(stderr, "%s", logg->getLastError()); if (child && child->socket) { if (sender) { // send the error, regardless of the command sent by Streamline - sender->writeData(logg->getLastError(), strlen(logg->getLastError()), RESPONSE_ERROR); + sender->writeData(logg.getLastError(), strlen(logg.getLastError()), RESPONSE_ERROR, true); // cannot close the socket before Streamline issues the command, so wait for the command before exiting - if (gSessionData->mWaitingOnCommand) { + if (gSessionData.mWaitingOnCommand) { char discard; child->socket->receiveNBytes(&discard, 1); } @@ -67,8 +65,9 @@ void handleException() { } } - if (gSessionData->mLocalCapture) + if (gSessionData.mLocalCapture) { cleanUp(); + } exit(1); } @@ -77,11 +76,11 @@ void handleException() { static void child_handler(int signum) { static bool beenHere = false; if (beenHere == true) { - logg->logMessage("Gator is being forced to shut down."); + logg.logMessage("Gator is being forced to shut down."); exit(1); } beenHere = true; - logg->logMessage("Gator is shutting down."); + logg.logMessage("Gator is shutting down."); if (signum == SIGALRM || !primarySource) { exit(1); } else { @@ -93,16 +92,16 @@ static void child_handler(int signum) { static void *durationThread(void *) { prctl(PR_SET_NAME, (unsigned long)&"gatord-duration", 0, 0, 0); sem_wait(&startProfile); - if (gSessionData->mSessionIsActive) { + if (gSessionData.mSessionIsActive) { // Time out after duration seconds // Add a second for host-side filtering - sleep(gSessionData->mDuration + 1); - if (gSessionData->mSessionIsActive) { - logg->logMessage("Duration expired."); + sleep(gSessionData.mDuration + 1); + if (gSessionData.mSessionIsActive) { + logg.logMessage("Duration expired."); child->endSession(); } } - logg->logMessage("Exit duration thread"); + logg.logMessage("Exit duration thread"); return 0; } @@ -110,7 +109,7 @@ static void *stopThread(void *) { OlySocket* socket = child->socket; prctl(PR_SET_NAME, (unsigned long)&"gatord-stopper", 0, 0, 0); - while (gSessionData->mSessionIsActive) { + while (gSessionData.mSessionIsActive) { // This thread will stall until the APC_STOP or PING command is received over the socket or the socket is disconnected unsigned char header[5]; const int result = socket->receiveNBytes((char*)&header, sizeof(header)); @@ -120,26 +119,26 @@ static void *stopThread(void *) { child->endSession(); } else if (result > 0) { if ((type != COMMAND_APC_STOP) && (type != COMMAND_PING)) { - logg->logMessage("INVESTIGATE: Received unknown command type %d", type); + logg.logMessage("INVESTIGATE: Received unknown command type %d", type); } else { // verify a length of zero if (length == 0) { if (type == COMMAND_APC_STOP) { - logg->logMessage("Stop command received."); + logg.logMessage("Stop command received."); child->endSession(); } else { // Ping is used to make sure gator is alive and requires an ACK as the response - logg->logMessage("Ping command received."); + logg.logMessage("Ping command received."); sender->writeData(NULL, 0, RESPONSE_ACK); } } else { - logg->logMessage("INVESTIGATE: Received stop command but with length = %d", length); + logg.logMessage("INVESTIGATE: Received stop command but with length = %d", length); } } } } - logg->logMessage("Exit stop thread"); + logg.logMessage("Exit stop thread"); return 0; } @@ -150,34 +149,30 @@ static void *senderThread(void *) { prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0); sem_wait(&haltPipeline); - while (!primarySource->isDone() || - !externalSource->isDone() || + while (!externalSource->isDone() || (userSpaceSource != NULL && !userSpaceSource->isDone()) || - (ftraceSource != NULL && !ftraceSource->isDone())) { + !primarySource->isDone()) { sem_wait(&senderSem); - primarySource->write(sender); externalSource->write(sender); if (userSpaceSource != NULL) { userSpaceSource->write(sender); } - if (ftraceSource != NULL) { - ftraceSource->write(sender); - } + primarySource->write(sender); } // write end-of-capture sequence - if (!gSessionData->mLocalCapture) { + if (!gSessionData.mLocalCapture) { sender->writeData(end_sequence, sizeof(end_sequence), RESPONSE_APC_DATA); } - logg->logMessage("Exit sender thread"); + logg.logMessage("Exit sender thread"); return 0; } Child::Child() { initialization(); - gSessionData->mLocalCapture = true; + gSessionData.mLocalCapture = true; } Child::Child(OlySocket* sock, int conn) { @@ -191,7 +186,7 @@ Child::~Child() { void Child::initialization() { // Set up different handlers for signals - gSessionData->mSessionIsActive = true; + gSessionData.mSessionIsActive = true; signal(SIGINT, child_handler); signal(SIGTERM, child_handler); signal(SIGABRT, child_handler); @@ -207,15 +202,12 @@ void Child::initialization() { } void Child::endSession() { - gSessionData->mSessionIsActive = false; + gSessionData.mSessionIsActive = false; primarySource->interrupt(); externalSource->interrupt(); if (userSpaceSource != NULL) { userSpaceSource->interrupt(); } - if (ftraceSource != NULL) { - ftraceSource->interrupt(); - } sem_post(&haltPipeline); } @@ -232,15 +224,15 @@ void Child::run() { sender = new Sender(socket); if (mNumConnections > 1) { - logg->logError(__FILE__, __LINE__, "Session already in progress"); + logg.logError("Session already in progress"); handleException(); } // Populate gSessionData with the configuration { ConfigurationXML configuration; } - // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated - if (!gSessionData->perf.isSetup()) { + // Set up the driver; must be done after gSessionData.mPerfCounterType[] is populated + if (!gSessionData.mPerf.isSetup()) { primarySource = new DriverSource(&senderSem, &startProfile); } else { primarySource = new PerfSource(&senderSem, &startProfile); @@ -253,7 +245,7 @@ void Child::run() { // Set up counters using the associated driver's setup function for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { - Counter & counter = gSessionData->mCounters[i]; + Counter & counter = gSessionData.mCounters[i]; if (counter.isEnabled()) { counter.getDriver()->setupCounter(counter); } @@ -265,49 +257,51 @@ void Child::run() { StreamlineSetup ss(socket); } else { char* xmlString; - xmlString = util->readFromDisk(gSessionData->mSessionXMLPath); + xmlString = readFromDisk(gSessionData.mSessionXMLPath); if (xmlString == 0) { - logg->logError(__FILE__, __LINE__, "Unable to read session xml file: %s", gSessionData->mSessionXMLPath); + logg.logError("Unable to read session xml file: %s", gSessionData.mSessionXMLPath); handleException(); } - gSessionData->parseSessionXML(xmlString); + gSessionData.parseSessionXML(xmlString); localCapture = new LocalCapture(); - localCapture->createAPCDirectory(gSessionData->mTargetPath); - localCapture->copyImages(gSessionData->mImages); + localCapture->createAPCDirectory(gSessionData.mTargetPath); + localCapture->copyImages(gSessionData.mImages); localCapture->write(xmlString); - sender->createDataFile(gSessionData->mAPCDir); + sender->createDataFile(gSessionData.mAPCDir); free(xmlString); } - if (gSessionData->kmod.isMaliCapture() && (gSessionData->mSampleRate == 0)) { - logg->logError(__FILE__, __LINE__, "Mali counters are not supported with Sample Rate: None."); + if (gSessionData.mKmod.isMaliCapture() && (gSessionData.mSampleRate == 0)) { + logg.logError("Mali counters are not supported with Sample Rate: None."); + handleException(); + } + + // Initialize ftrace source before child as it's slow and dependens on nothing else + // If initialized later, us gator with ftrace has time sync issues + // Must be initialized before senderThread is started as senderThread checks externalSource + externalSource = new ExternalSource(&senderSem); + if (!externalSource->prepare()) { + logg.logError("Unable to prepare external source for capture"); handleException(); } + externalSource->start(); // Must be after session XML is parsed if (!primarySource->prepare()) { - if (gSessionData->perf.isSetup()) { - logg->logError(__FILE__, __LINE__, "Unable to prepare gator driver for capture"); + if (gSessionData.mPerf.isSetup()) { + logg.logError("Unable to communicate with the perf API, please ensure that CONFIG_TRACING and CONFIG_CONTEXT_SWITCH_TRACER are enabled. Please refer to streamline/gator/README.md for more information."); } else { - logg->logError(__FILE__, __LINE__, "Unable to communicate with the perf API, please ensure that CONFIG_TRACING and CONFIG_CONTEXT_SWITCH_TRACER are enabled. Please refer to README_Streamline.txt for more information."); + logg.logError("Unable to prepare gator driver for capture"); } handleException(); } // Sender thread shall be halted until it is signaled for one shot mode - sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2); - - // Must be initialized before senderThread is started as senderThread checks externalSource - externalSource = new ExternalSource(&senderSem); - if (!externalSource->prepare()) { - logg->logError(__FILE__, __LINE__, "Unable to prepare external source for capture"); - handleException(); - } - externalSource->start(); + sem_init(&haltPipeline, 0, gSessionData.mOneShot ? 0 : 2); // Create the duration, stop, and sender threads bool thread_creation_success = true; - if (gSessionData->mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) { + if (gSessionData.mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) { thread_creation_success = false; } else if (socket && pthread_create(&stopThreadID, NULL, stopThread, NULL)) { thread_creation_success = false; @@ -316,30 +310,21 @@ void Child::run() { } bool startUSSource = false; - for (int i = 0; i < ARRAY_LENGTH(gSessionData->usDrivers); ++i) { - if (gSessionData->usDrivers[i]->countersEnabled()) { + for (int i = 0; i < ARRAY_LENGTH(gSessionData.mUsDrivers); ++i) { + if (gSessionData.mUsDrivers[i]->countersEnabled()) { startUSSource = true; } } if (startUSSource) { userSpaceSource = new UserSpaceSource(&senderSem); if (!userSpaceSource->prepare()) { - logg->logError(__FILE__, __LINE__, "Unable to prepare userspace source for capture"); + logg.logError("Unable to prepare userspace source for capture"); handleException(); } userSpaceSource->start(); } - if (gSessionData->ftraceDriver.countersEnabled()) { - ftraceSource = new FtraceSource(&senderSem); - if (!ftraceSource->prepare()) { - logg->logError(__FILE__, __LINE__, "Unable to prepare userspace source for capture"); - handleException(); - } - ftraceSource->start(); - } - - if (gSessionData->mAllowCommands && (gSessionData->mCaptureCommand != NULL)) { + if (gSessionData.mAllowCommands && (gSessionData.mCaptureCommand != NULL)) { pthread_t thread; if (pthread_create(&thread, NULL, commandThread, NULL)) { thread_creation_success = false; @@ -347,7 +332,7 @@ void Child::run() { } if (!thread_creation_success) { - logg->logError(__FILE__, __LINE__, "Failed to create gator threads"); + logg.logError("Failed to create gator threads"); handleException(); } @@ -357,33 +342,28 @@ void Child::run() { // Start profiling primarySource->run(); - if (ftraceSource != NULL) { - ftraceSource->join(); - } + // Wait for the other threads to exit if (userSpaceSource != NULL) { userSpaceSource->join(); } externalSource->join(); - - // Wait for the other threads to exit pthread_join(senderThreadID, NULL); // Shutting down the connection should break the stop thread which is stalling on the socket recv() function if (socket) { - logg->logMessage("Waiting on stop thread"); + logg.logMessage("Waiting on stop thread"); socket->shutdownConnection(); pthread_join(stopThreadID, NULL); } // Write the captured xml file - if (gSessionData->mLocalCapture) { + if (gSessionData.mLocalCapture) { CapturedXML capturedXML; - capturedXML.write(gSessionData->mAPCDir); + capturedXML.write(gSessionData.mAPCDir); } - logg->logMessage("Profiling ended."); + logg.logMessage("Profiling ended."); - delete ftraceSource; delete userSpaceSource; delete externalSource; delete primarySource; diff --git a/tools/gator/daemon/Child.h b/tools/gator/daemon/Child.h index cc78202..a6c54db 100644 --- a/tools/gator/daemon/Child.h +++ b/tools/gator/daemon/Child.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 diff --git a/tools/gator/daemon/Command.cpp b/tools/gator/daemon/Command.cpp index 28d73cf..e9f2304 100644 --- a/tools/gator/daemon/Command.cpp +++ b/tools/gator/daemon/Command.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2014. All rights reserved. + * Copyright (C) ARM Limited 2014-2015. 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 @@ -9,6 +9,7 @@ #include "Command.h" #include +#include #include #include #include @@ -23,14 +24,14 @@ #include "Logging.h" #include "SessionData.h" -static int getUid(const char *const name, char *const shPath, const char *const tmpDir) { +static int getUid(const char *const name, const char *const tmpDir, uid_t *const uid) { // Lookups may fail when using a different libc or a statically compiled executable char gatorTemp[32]; snprintf(gatorTemp, sizeof(gatorTemp), "%s/gator_temp", tmpDir); const int fd = open(gatorTemp, 600, O_CREAT | O_CLOEXEC); if (fd < 0) { - return -1; + return false; } close(fd); @@ -39,90 +40,77 @@ static int getUid(const char *const name, char *const shPath, const char *const const int pid = fork(); if (pid < 0) { - logg->logError(__FILE__, __LINE__, "fork failed"); + logg.logError("fork failed"); handleException(); } if (pid == 0) { - char cargv1[] = "-c"; - char *cargv[] = { - shPath, - cargv1, - cmd, - NULL, - }; - - execv(cargv[0], cargv); + execlp("sh", "sh", "-c", cmd, NULL); exit(-1); } while ((waitpid(pid, NULL, 0) < 0) && (errno == EINTR)); struct stat st; int result = -1; - if (stat(gatorTemp, &st) == 0) { - result = st.st_uid; + if (stat(gatorTemp, &st) != 0) { + return false; } + result = st.st_uid; unlink(gatorTemp); - return result; + *uid = result; + return true; } -static int getUid(const char *const name) { +static bool getUid(const char *const name, uid_t *const uid, gid_t *const gid) { // Look up the username struct passwd *const user = getpwnam(name); if (user != NULL) { - return user->pw_uid; + *uid = user->pw_uid; + *gid = user->pw_gid; + return true; } + // Unable to get the user without getpwanm, so create a unique uid by adding a fixed number to the pid + *gid = 0x484560f8 + getpid(); // Are we on Linux - char cargv0l[] = "/bin/sh"; - if ((access(cargv0l, X_OK) == 0) && (access("/tmp", W_OK) == 0)) { - return getUid(name, cargv0l, "/tmp"); + if (access("/tmp", W_OK) == 0) { + return getUid(name, "/tmp", uid); } // Are we on android - char cargv0a[] = "/system/bin/sh"; - if ((access(cargv0a, X_OK) == 0) && (access("/data", W_OK) == 0)) { - return getUid(name, cargv0a, "/data"); + if (access("/data", W_OK) == 0) { + return getUid(name, "/data", uid); } - return -1; + return false; } void *commandThread(void *) { prctl(PR_SET_NAME, (unsigned long)&"gatord-command", 0, 0, 0); - const char *const name = gSessionData->mCaptureUser == NULL ? "nobody" : gSessionData->mCaptureUser; - const int uid = getUid(name); - if (uid < 0) { - logg->logError(__FILE__, __LINE__, "Unable to lookup the user %s, please double check that the user exists", name); + const char *const name = gSessionData.mCaptureUser == NULL ? "nobody" : gSessionData.mCaptureUser; + uid_t uid; + gid_t gid; + if (!getUid(name, &uid, &gid)) { + logg.logError("Unable to look up the user %s, please double check that the user exists", name); handleException(); } sleep(3); - char buf[128]; + char buf[1<<8]; int pipefd[2]; if (pipe_cloexec(pipefd) != 0) { - logg->logError(__FILE__, __LINE__, "pipe failed"); + logg.logError("pipe failed"); handleException(); } const int pid = fork(); if (pid < 0) { - logg->logError(__FILE__, __LINE__, "fork failed"); + logg.logError("fork failed"); handleException(); } if (pid == 0) { - char cargv0l[] = "/bin/sh"; - char cargv0a[] = "/system/bin/sh"; - char cargv1[] = "-c"; - char *cargv[] = { - cargv0l, - cargv1, - gSessionData->mCaptureCommand, - NULL, - }; - buf[0] = '\0'; close(pipefd[0]); @@ -132,22 +120,28 @@ void *commandThread(void *) { goto fail_exit; } - if (setuid(uid) != 0) { - snprintf(buf, sizeof(buf), "setuid failed"); + if (setgroups(1, &gid) != 0) { + snprintf(buf, sizeof(buf), "setgroups failed"); + goto fail_exit; + } + if (setresgid(gid, gid, gid) != 0) { + snprintf(buf, sizeof(buf), "setresgid failed"); + goto fail_exit; + } + if (setresuid(uid, uid, uid) != 0) { + snprintf(buf, sizeof(buf), "setresuid failed"); goto fail_exit; } { - const char *const path = gSessionData->mCaptureWorkingDir == NULL ? "/" : gSessionData->mCaptureWorkingDir; + const char *const path = gSessionData.mCaptureWorkingDir == NULL ? "/" : gSessionData.mCaptureWorkingDir; if (chdir(path) != 0) { - snprintf(buf, sizeof(buf), "Unable to cd to %s, please verify the directory exists and is accessable to %s", path, name); + snprintf(buf, sizeof(buf), "Unable to cd to %s, please verify the directory exists and is accessible to %s", path, name); goto fail_exit; } } - execv(cargv[0], cargv); - cargv[0] = cargv0a; - execv(cargv[0], cargv); + execlp("sh", "sh", "-c", gSessionData.mCaptureCommand, NULL); snprintf(buf, sizeof(buf), "execv failed"); fail_exit: @@ -163,7 +157,7 @@ void *commandThread(void *) { close(pipefd[1]); const ssize_t bytes = read(pipefd[0], buf, sizeof(buf)); if (bytes > 0) { - logg->logError(__FILE__, __LINE__, buf); + logg.logError("%s", buf); handleException(); } close(pipefd[0]); diff --git a/tools/gator/daemon/Command.h b/tools/gator/daemon/Command.h index 17244b7..2838adc 100644 --- a/tools/gator/daemon/Command.h +++ b/tools/gator/daemon/Command.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2014. All rights reserved. + * Copyright (C) ARM Limited 2014-2015. 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 diff --git a/tools/gator/daemon/Config.h b/tools/gator/daemon/Config.h index bee383a..3c6752e 100644 --- a/tools/gator/daemon/Config.h +++ b/tools/gator/daemon/Config.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -9,12 +9,19 @@ #ifndef CONFIG_H #define CONFIG_H +#define STRIFY2(ARG) #ARG +#define STRIFY(ARG) STRIFY2(ARG) + #define ARRAY_LENGTH(A) static_cast(sizeof(A)/sizeof((A)[0])) #define ACCESS_ONCE(x) (*(volatile typeof(x)*)&(x)) #define MAX_PERFORMANCE_COUNTERS 50 #define NR_CPUS 32 +// If debugfs is not mounted at /sys/kernel/debug, update TRACING_PATH +#define TRACING_PATH "/sys/kernel/debug/tracing" +#define EVENTS_PATH TRACING_PATH "/events" + template static inline T min(const T a, const T b) { return (a < b ? a : b); diff --git a/tools/gator/daemon/ConfigurationXML.cpp b/tools/gator/daemon/ConfigurationXML.cpp index 6590dd3..eda487b 100644 --- a/tools/gator/daemon/ConfigurationXML.cpp +++ b/tools/gator/daemon/ConfigurationXML.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -17,13 +17,18 @@ #include "OlyUtility.h" #include "SessionData.h" -static const char* ATTR_COUNTER = "counter"; -static const char* ATTR_REVISION = "revision"; -static const char* ATTR_EVENT = "event"; -static const char* ATTR_COUNT = "count"; -static const char* ATTR_CORES = "cores"; +static const char ATTR_COUNTER[] = "counter"; +static const char ATTR_REVISION[] = "revision"; +static const char ATTR_EVENT[] = "event"; +static const char ATTR_COUNT[] = "count"; +static const char ATTR_CORES[] = "cores"; ConfigurationXML::ConfigurationXML() { + if (gSessionData.mCountersError != NULL) { + free(gSessionData.mCountersError); + gSessionData.mCountersError = NULL; + } + const char * configuration_xml; unsigned int configuration_xml_len; getDefaultConfigurationXml(configuration_xml, configuration_xml_len); @@ -31,11 +36,11 @@ ConfigurationXML::ConfigurationXML() { char path[PATH_MAX]; getPath(path); - mConfigurationXML = util->readFromDisk(path); + mConfigurationXML = readFromDisk(path); for (int retryCount = 0; retryCount < 2; ++retryCount) { if (mConfigurationXML == NULL) { - logg->logMessage("Unable to locate configuration.xml, using default in binary"); + logg.logMessage("Unable to locate configuration.xml, using default in binary"); // null-terminate configuration_xml mConfigurationXML = (char*)malloc(configuration_xml_len + 1); memcpy(mConfigurationXML, (const void*)configuration_xml, configuration_xml_len); @@ -68,14 +73,12 @@ int ConfigurationXML::parse(const char* configurationXML) { mxml_node_t *tree, *node; int ret; - // clear counter overflow - gSessionData->mCounterOverflow = 0; - gSessionData->mIsEBS = false; + gSessionData.mIsEBS = false; mIndex = 0; // disable all counters prior to parsing the configuration xml for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { - gSessionData->mCounters[i].setEnabled(false); + gSessionData.mCounters[i].setEnabled(false); } tree = mxmlLoadString(NULL, configurationXML, MXML_NO_CALLBACK); @@ -98,26 +101,40 @@ int ConfigurationXML::parse(const char* configurationXML) { mxmlDelete(tree); + if (gSessionData.mCountersError == NULL && mIndex > MAX_PERFORMANCE_COUNTERS) { + if (asprintf(&gSessionData.mCountersError, "Only %i performance counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, mIndex) <= 0) { + logg.logError("asprintf failed"); + handleException(); + } + } + gSessionData.mCcnDriver.validateCounters(); + return ret; } void ConfigurationXML::validate(void) { for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { - const Counter & counter = gSessionData->mCounters[i]; + const Counter & counter = gSessionData.mCounters[i]; if (counter.isEnabled()) { if (strcmp(counter.getType(), "") == 0) { - logg->logError(__FILE__, __LINE__, "Invalid required attribute in configuration.xml:\n counter=\"%s\"\n event=%d\n", counter.getType(), counter.getEvent()); - handleException(); + if (gSessionData.mCountersError == NULL && asprintf(&gSessionData.mCountersError, "Invalid required attribute in configuration.xml:\n counter=\"%s\"\n event=%d", counter.getType(), counter.getEvent()) <= 0) { + logg.logError("asprintf failed"); + handleException(); + } + return; } // iterate through the remaining enabled performance counters for (int j = i + 1; j < MAX_PERFORMANCE_COUNTERS; j++) { - const Counter & counter2 = gSessionData->mCounters[j]; + const Counter & counter2 = gSessionData.mCounters[j]; if (counter2.isEnabled()) { // check if the types are the same if (strcmp(counter.getType(), counter2.getType()) == 0) { - logg->logError(__FILE__, __LINE__, "Duplicate performance counter type in configuration.xml: %s", counter.getType()); - handleException(); + if (gSessionData.mCountersError == NULL && asprintf(&gSessionData.mCountersError, "Duplicate performance counter type in configuration.xml: %s", counter.getType()) <= 0) { + logg.logError("asprintf failed"); + handleException(); + } + return; } } } @@ -149,19 +166,18 @@ void ConfigurationXML::configurationTag(mxml_node_t *node) { // handle all other performance counters if (mIndex >= MAX_PERFORMANCE_COUNTERS) { mIndex++; - gSessionData->mCounterOverflow = mIndex; return; } // read attributes - Counter & counter = gSessionData->mCounters[mIndex]; + Counter & counter = gSessionData.mCounters[mIndex]; counter.clear(); if (mxmlElementGetAttr(node, ATTR_COUNTER)) counter.setType(mxmlElementGetAttr(node, ATTR_COUNTER)); if (mxmlElementGetAttr(node, ATTR_EVENT)) counter.setEvent(strtol(mxmlElementGetAttr(node, ATTR_EVENT), NULL, 16)); if (mxmlElementGetAttr(node, ATTR_COUNT)) counter.setCount(strtol(mxmlElementGetAttr(node, ATTR_COUNT), NULL, 10)); if (mxmlElementGetAttr(node, ATTR_CORES)) counter.setCores(strtol(mxmlElementGetAttr(node, ATTR_CORES), NULL, 10)); if (counter.getCount() > 0) { - gSessionData->mIsEBS = true; + gSessionData.mIsEBS = true; } counter.setEnabled(true); @@ -169,7 +185,7 @@ void ConfigurationXML::configurationTag(mxml_node_t *node) { for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { if (driver->claimCounter(counter)) { if (counter.getDriver() != NULL) { - logg->logError(__FILE__, __LINE__, "More than one driver has claimed %s:%i", counter.getType(), counter.getEvent()); + logg.logError("More than one driver has claimed %s:%i", counter.getType(), counter.getEvent()); handleException(); } counter.setDriver(driver); @@ -178,7 +194,7 @@ void ConfigurationXML::configurationTag(mxml_node_t *node) { // If no driver is associated with the counter, disable it if (counter.getDriver() == NULL) { - logg->logMessage("No driver has claimed %s:%i", counter.getType(), counter.getEvent()); + logg.logMessage("No driver has claimed %s:%i", counter.getType(), counter.getEvent()); counter.setEnabled(false); } @@ -195,11 +211,11 @@ void ConfigurationXML::getDefaultConfigurationXml(const char * & xml, unsigned i } void ConfigurationXML::getPath(char* path) { - if (gSessionData->mConfigurationXMLPath) { - strncpy(path, gSessionData->mConfigurationXMLPath, PATH_MAX); + if (gSessionData.mConfigurationXMLPath) { + strncpy(path, gSessionData.mConfigurationXMLPath, PATH_MAX); } else { - if (util->getApplicationFullPath(path, PATH_MAX) != 0) { - logg->logMessage("Unable to determine the full path of gatord, the cwd will be used"); + if (getApplicationFullPath(path, PATH_MAX) != 0) { + logg.logMessage("Unable to determine the full path of gatord, the cwd will be used"); } strncat(path, "configuration.xml", PATH_MAX - strlen(path) - 1); } @@ -210,8 +226,8 @@ void ConfigurationXML::remove() { getPath(path); if (::remove(path) != 0) { - logg->logError(__FILE__, __LINE__, "Invalid configuration.xml file detected and unable to delete it. To resolve, delete configuration.xml on disk"); + logg.logError("Invalid configuration.xml file detected and unable to delete it. To resolve, delete configuration.xml on disk"); handleException(); } - logg->logMessage("Invalid configuration.xml file detected and removed"); + logg.logMessage("Invalid configuration.xml file detected and removed"); } diff --git a/tools/gator/daemon/ConfigurationXML.h b/tools/gator/daemon/ConfigurationXML.h index efa415e..2677660 100644 --- a/tools/gator/daemon/ConfigurationXML.h +++ b/tools/gator/daemon/ConfigurationXML.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -20,12 +20,12 @@ public: ConfigurationXML(); ~ConfigurationXML(); const char* getConfigurationXML() {return mConfigurationXML;} - void validate(void); private: char* mConfigurationXML; int mIndex; + void validate(void); int parse(const char* xmlFile); int configurationsTag(mxml_node_t *node); void configurationTag(mxml_node_t *node); diff --git a/tools/gator/daemon/Counter.h b/tools/gator/daemon/Counter.h index 5202aa0..a4c22f5 100644 --- a/tools/gator/daemon/Counter.h +++ b/tools/gator/daemon/Counter.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 diff --git a/tools/gator/daemon/DiskIODriver.cpp b/tools/gator/daemon/DiskIODriver.cpp index 5deb0f3..6e72bfc 100644 --- a/tools/gator/daemon/DiskIODriver.cpp +++ b/tools/gator/daemon/DiskIODriver.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -53,7 +53,7 @@ DiskIODriver::~DiskIODriver() { void DiskIODriver::readEvents(mxml_node_t *const) { // Only for use with perf - if (!gSessionData->perf.isSetup()) { + if (!gSessionData.mPerf.isSetup()) { return; } @@ -67,7 +67,7 @@ void DiskIODriver::doRead() { } if (!mBuf.read("/proc/diskstats")) { - logg->logError(__FILE__, __LINE__, "Unable to read /proc/diskstats"); + logg.logError("Unable to read /proc/diskstats"); handleException(); } @@ -76,9 +76,9 @@ void DiskIODriver::doRead() { char *lastName = NULL; int lastNameLen = -1; - char *start = mBuf.getBuf(); - while (*start != '\0') { - char *end = strchr(start, '\n'); + char *line = mBuf.getBuf(); + while (*line != '\0') { + char *end = strchr(line, '\n'); if (end != NULL) { *end = '\0'; } @@ -87,15 +87,15 @@ void DiskIODriver::doRead() { int nameEnd = -1; int64_t readBytes = -1; int64_t writeBytes = -1; - const int count = sscanf(start, "%*d %*d %n%*s%n %*u %*u %" SCNu64 " %*u %*u %*u %" SCNu64, &nameStart, &nameEnd, &readBytes, &writeBytes); + const int count = sscanf(line, "%*d %*d %n%*s%n %*u %*u %" SCNu64 " %*u %*u %*u %" SCNu64, &nameStart, &nameEnd, &readBytes, &writeBytes); if (count != 2) { - logg->logError(__FILE__, __LINE__, "Unable to parse /proc/diskstats"); + logg.logError("Unable to parse /proc/diskstats"); handleException(); } // Skip partitions which are identified if the name is a substring of the last non-partition - if ((lastName == NULL) || (strncmp(lastName, start + nameStart, lastNameLen) != 0)) { - lastName = start + nameStart; + if ((lastName == NULL) || (strncmp(lastName, line + nameStart, lastNameLen) != 0)) { + lastName = line + nameStart; lastNameLen = nameEnd - nameStart; mReadBytes += readBytes; mWriteBytes += writeBytes; @@ -104,7 +104,7 @@ void DiskIODriver::doRead() { if (end == NULL) { break; } - start = end + 1; + line = end + 1; } } diff --git a/tools/gator/daemon/DiskIODriver.h b/tools/gator/daemon/DiskIODriver.h index d0db18c..6ecda5a 100644 --- a/tools/gator/daemon/DiskIODriver.h +++ b/tools/gator/daemon/DiskIODriver.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 diff --git a/tools/gator/daemon/Driver.cpp b/tools/gator/daemon/Driver.cpp index 275da31..872ab7b 100644 --- a/tools/gator/daemon/Driver.cpp +++ b/tools/gator/daemon/Driver.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -43,6 +43,9 @@ DriverCounter *SimpleDriver::findCounter(const Counter &counter) const { return NULL; } +Driver::~Driver() { +} + bool SimpleDriver::claimCounter(const Counter &counter) const { return findCounter(counter) != NULL; } diff --git a/tools/gator/daemon/Driver.h b/tools/gator/daemon/Driver.h index 72870e3..fe20471 100644 --- a/tools/gator/daemon/Driver.h +++ b/tools/gator/daemon/Driver.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -43,7 +43,7 @@ class Driver { public: static Driver *getHead() { return head; } - virtual ~Driver() {} + virtual ~Driver(); // Returns true if this driver can manage the counter virtual bool claimCounter(const Counter &counter) const = 0; diff --git a/tools/gator/daemon/DriverSource.cpp b/tools/gator/daemon/DriverSource.cpp index 7f299b6..92cc0d9 100644 --- a/tools/gator/daemon/DriverSource.cpp +++ b/tools/gator/daemon/DriverSource.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -28,47 +28,22 @@ extern Child *child; DriverSource::DriverSource(sem_t *senderSem, sem_t *startProfile) : mBuffer(NULL), mFifo(NULL), mSenderSem(senderSem), mStartProfile(startProfile), mBufferSize(0), mBufferFD(0), mLength(1) { - int driver_version = 0; - mBuffer = new Buffer(0, FRAME_PERF_ATTRS, 4*1024*1024, senderSem); - if (readIntDriver("/dev/gator/version", &driver_version) == -1) { - logg->logError(__FILE__, __LINE__, "Error reading gator driver version"); - handleException(); - } - - // Verify the driver version matches the daemon version - if (driver_version != PROTOCOL_VERSION) { - if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) { - // One of the mismatched versions is development version - logg->logError(__FILE__, __LINE__, - "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n" - ">> The following must be synchronized from engineering repository:\n" - ">> * gator driver\n" - ">> * gator daemon\n" - ">> * Streamline", driver_version, PROTOCOL_VERSION); - handleException(); - } else { - // Release version mismatch - logg->logError(__FILE__, __LINE__, - "gator driver version \"%d\" is different than gator daemon version \"%d\".\n" - ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION); - handleException(); - } - } + checkVersion(); int enable = -1; if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) { - logg->logError(__FILE__, __LINE__, "Driver already enabled, possibly a session is already in progress."); + logg.logError("Driver already enabled, possibly a session is already in progress."); handleException(); } - readIntDriver("/dev/gator/cpu_cores", &gSessionData->mCores); - if (gSessionData->mCores == 0) { - gSessionData->mCores = 1; + readIntDriver("/dev/gator/cpu_cores", &gSessionData.mCores); + if (gSessionData.mCores == 0) { + gSessionData.mCores = 1; } if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) { - logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size"); + logg.logError("Unable to read the driver buffer size"); handleException(); } } @@ -85,10 +60,39 @@ DriverSource::~DriverSource() { } } +void DriverSource::checkVersion() { + int driverVersion = 0; + + if (readIntDriver("/dev/gator/version", &driverVersion) == -1) { + logg.logError("Error reading gator driver version"); + handleException(); + } + + // Verify the driver version matches the daemon version + if (driverVersion != PROTOCOL_VERSION) { + if ((driverVersion > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) { + // One of the mismatched versions is development version + logg.logError( + "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n" + ">> The following must be synchronized from engineering repository:\n" + ">> * gator driver\n" + ">> * gator daemon\n" + ">> * Streamline", driverVersion, PROTOCOL_VERSION); + handleException(); + } else { + // Release version mismatch + logg.logError( + "gator driver version \"%d\" is different than gator daemon version \"%d\".\n" + ">> Please upgrade the driver and daemon to the latest versions.", driverVersion, PROTOCOL_VERSION); + handleException(); + } + } +} + bool DriverSource::prepare() { // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length - logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, mBufferSize); - mFifo = new Fifo(mBufferSize + 5, gSessionData->mTotalBufferSize*1024*1024, mSenderSem); + logg.logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData.mTotalBufferSize, mBufferSize); + mFifo = new Fifo(mBufferSize + 5, gSessionData.mTotalBufferSize*1024*1024, mSenderSem); return true; } @@ -99,10 +103,11 @@ void DriverSource::bootstrapThread() { DynBuf printb; DynBuf b1; DynBuf b2; - const uint64_t currTime = getTime(); + // MonotonicStarted may not be not assigned yet + const uint64_t currTime = 0;//getTime() - gSessionData.mMonotonicStarted; - if (!readProcComms(currTime, mBuffer, &printb, &b1, &b2)) { - logg->logError(__FILE__, __LINE__, "readProcComms failed"); + if (!readProcSysDependencies(currTime, mBuffer, &printb, &b1, &b2)) { + logg.logError("readProcSysDependencies failed"); handleException(); } @@ -120,45 +125,45 @@ void DriverSource::run() { char *collectBuffer = mFifo->start(); int bytesCollected = 0; - logg->logMessage("********** Profiling started **********"); + logg.logMessage("********** Profiling started **********"); // Set the maximum backtrace depth - if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) { - logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth"); + if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData.mBacktraceDepth)) { + logg.logError("Unable to set the driver backtrace depth"); handleException(); } // open the buffer which calls userspace_buffer_open() in the driver mBufferFD = open("/dev/gator/buffer", O_RDONLY | O_CLOEXEC); if (mBufferFD < 0) { - logg->logError(__FILE__, __LINE__, "The gator driver did not set up properly. Please view the linux console or dmesg log for more information on the failure."); + logg.logError("The gator driver did not set up properly. Please view the linux console or dmesg log for more information on the failure."); handleException(); } // set the tick rate of the profiling timer - if (writeReadDriver("/dev/gator/tick", &gSessionData->mSampleRate) != 0) { - logg->logError(__FILE__, __LINE__, "Unable to set the driver tick"); + if (writeReadDriver("/dev/gator/tick", &gSessionData.mSampleRate) != 0) { + logg.logError("Unable to set the driver tick"); handleException(); } // notify the kernel of the response type - int response_type = gSessionData->mLocalCapture ? 0 : RESPONSE_APC_DATA; + int response_type = gSessionData.mLocalCapture ? 0 : RESPONSE_APC_DATA; if (writeDriver("/dev/gator/response_type", response_type)) { - logg->logError(__FILE__, __LINE__, "Unable to write the response type"); + logg.logError("Unable to write the response type"); handleException(); } // Set the live rate - if (writeReadDriver("/dev/gator/live_rate", &gSessionData->mLiveRate)) { - logg->logError(__FILE__, __LINE__, "Unable to set the driver live rate"); + if (writeReadDriver("/dev/gator/live_rate", &gSessionData.mLiveRate)) { + logg.logError("Unable to set the driver live rate"); handleException(); } - logg->logMessage("Start the driver"); + logg.logMessage("Start the driver"); // This command makes the driver start profiling by calling gator_op_start() in the driver if (writeDriver("/dev/gator/enable", "1") != 0) { - logg->logError(__FILE__, __LINE__, "The gator driver did not start properly. Please view the linux console or dmesg log for more information on the failure."); + logg.logError("The gator driver did not start properly. Please view the linux console or dmesg log for more information on the failure."); handleException(); } @@ -168,7 +173,7 @@ void DriverSource::run() { pthread_t bootstrapThreadID; if (pthread_create(&bootstrapThreadID, NULL, bootstrapThreadStatic, this) != 0) { - logg->logError(__FILE__, __LINE__, "Unable to start the gator_bootstrap thread"); + logg.logError("Unable to start the gator_bootstrap thread"); handleException(); } @@ -185,19 +190,19 @@ void DriverSource::run() { } // return the total bytes written - logg->logMessage("Driver read of %d bytes", bytesCollected); + logg.logMessage("Driver read of %d bytes", bytesCollected); // In one shot mode, stop collection once all the buffers are filled - if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { + if (gSessionData.mOneShot && gSessionData.mSessionIsActive) { if (bytesCollected == -1 || mFifo->willFill(bytesCollected)) { - logg->logMessage("One shot"); + logg.logMessage("One shot (gator.ko)"); child->endSession(); } } collectBuffer = mFifo->write(bytesCollected); } while (bytesCollected > 0); - logg->logMessage("Exit collect data loop"); + logg.logMessage("Exit collect data loop"); pthread_join(bootstrapThreadID, NULL); } @@ -205,7 +210,7 @@ void DriverSource::run() { void DriverSource::interrupt() { // This command should cause the read() function in collect() to return and stop the driver from profiling if (writeDriver("/dev/gator/enable", "0") != 0) { - logg->logMessage("Stopping kernel failed"); + logg.logMessage("Stopping kernel failed"); } } @@ -219,7 +224,7 @@ void DriverSource::write(Sender *sender) { sender->writeData(data, mLength, RESPONSE_APC_DATA); mFifo->release(); // Assume the summary packet is in the first block received from the driver - gSessionData->mSentSummary = true; + gSessionData.mSentSummary = true; } if (mBuffer != NULL && !mBuffer->isDone()) { mBuffer->write(sender); @@ -249,7 +254,7 @@ int DriverSource::readIntDriver(const char *fullpath, int *value) { errno = 0; *value = strtol(data, &endptr, 10); if (errno != 0 || *endptr != '\n') { - logg->logMessage("Invalid value in file %s", fullpath); + logg.logMessage("Invalid value in file %s", fullpath); return -1; } @@ -274,7 +279,7 @@ int DriverSource::readInt64Driver(const char *fullpath, int64_t *value) { errno = 0; *value = strtoll(data, &endptr, 10); if (errno != 0 || (*endptr != '\n' && *endptr != '\0')) { - logg->logMessage("Invalid value in file %s", fullpath); + logg.logMessage("Invalid value in file %s", fullpath); return -1; } @@ -288,7 +293,7 @@ int DriverSource::writeDriver(const char *fullpath, const char *data) { } if (::write(fd, data, strlen(data)) < 0) { close(fd); - logg->logMessage("Opened but could not write to %s", fullpath); + logg.logMessage("Opened but could not write to %s", fullpath); return -1; } close(fd); diff --git a/tools/gator/daemon/DriverSource.h b/tools/gator/daemon/DriverSource.h index ec27b08..971a4d9 100644 --- a/tools/gator/daemon/DriverSource.h +++ b/tools/gator/daemon/DriverSource.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -29,6 +29,7 @@ public: bool isDone(); void write(Sender *sender); + static void checkVersion(); static int readIntDriver(const char *fullpath, int *value); static int readInt64Driver(const char *fullpath, int64_t *value); static int writeDriver(const char *fullpath, const char *data); diff --git a/tools/gator/daemon/DynBuf.cpp b/tools/gator/daemon/DynBuf.cpp index df20713..c5cc60d 100644 --- a/tools/gator/daemon/DynBuf.cpp +++ b/tools/gator/daemon/DynBuf.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -10,8 +10,8 @@ #include #include -#include #include +#include #include #include "Logging.h" @@ -42,7 +42,7 @@ bool DynBuf::read(const char *const path) { const int fd = open(path, O_RDONLY | O_CLOEXEC); if (fd < 0) { - logg->logMessage("%s(%s:%i): open failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("open failed"); return false; } @@ -52,14 +52,14 @@ bool DynBuf::read(const char *const path) { const size_t minCapacity = length + MIN_BUFFER_FREE + 1; if (capacity < minCapacity) { if (resize(minCapacity) != 0) { - logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("DynBuf::resize failed"); goto fail; } } const ssize_t bytes = ::read(fd, buf + length, capacity - length - 1); if (bytes < 0) { - logg->logMessage("%s(%s:%i): read failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("read failed"); goto fail; } else if (bytes == 0) { break; @@ -102,38 +102,84 @@ int DynBuf::readlink(const char *const path) { bool DynBuf::printf(const char *format, ...) { va_list ap; + bool result; + + length = 0; + + va_start(ap, format); + result = append(format, ap); + va_end(ap); + + return result; +} + +bool DynBuf::append(const char *format, ...) { + va_list ap; + bool result; + + va_start(ap, format); + result = append(format, ap); + va_end(ap); + + return result; +} + +bool DynBuf::append(const char *format, va_list ap) { + va_list dup; if (capacity <= 0) { if (resize(2 * MIN_BUFFER_FREE) != 0) { - logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("DynBuf::resize failed"); return false; } } - va_start(ap, format); - int bytes = vsnprintf(buf, capacity, format, ap); - va_end(ap); + va_copy(dup, ap); + int bytes = vsnprintf(buf + length, capacity - length, format, dup); if (bytes < 0) { - logg->logMessage("%s(%s:%i): fsnprintf failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("fsnprintf failed"); return false; } + bytes += length; - if (static_cast(bytes) > capacity) { + if (static_cast(bytes) >= capacity) { if (resize(bytes + 1) != 0) { - logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("DynBuf::resize failed"); return false; } - va_start(ap, format); - bytes = vsnprintf(buf, capacity, format, ap); - va_end(ap); + bytes = vsnprintf(buf + length, capacity - length, format, ap); if (bytes < 0) { - logg->logMessage("%s(%s:%i): fsnprintf failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("fsnprintf failed"); return false; } + bytes += length; } length = bytes; return true; } + +bool DynBuf::appendStr(const char *str) { + if (capacity <= 0) { + if (resize(2 * MIN_BUFFER_FREE) != 0) { + logg.logMessage("DynBuf::resize failed"); + return false; + } + } + + size_t bytes = strlen(str); + if (length + bytes >= capacity) { + if (resize(length + bytes + 1) != 0) { + logg.logMessage("DynBuf::resize failed"); + return false; + } + } + + memcpy(buf + length, str, bytes + 1); + + length += bytes; + + return true; +} diff --git a/tools/gator/daemon/DynBuf.h b/tools/gator/daemon/DynBuf.h index 2f4554a..67c06ba 100644 --- a/tools/gator/daemon/DynBuf.h +++ b/tools/gator/daemon/DynBuf.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -9,6 +9,7 @@ #ifndef DYNBUF_H #define DYNBUF_H +#include #include class DynBuf { @@ -32,6 +33,10 @@ public: int readlink(const char *const path); __attribute__ ((format(printf, 2, 3))) bool printf(const char *format, ...); + __attribute__ ((format(printf, 2, 3))) + bool append(const char *format, ...); + bool append(const char *format, va_list ap); + bool appendStr(const char *str); size_t getLength() const { return length; } const char *getBuf() const { return buf; } diff --git a/tools/gator/daemon/EventsXML.cpp b/tools/gator/daemon/EventsXML.cpp index d905bba..4f44e04 100644 --- a/tools/gator/daemon/EventsXML.cpp +++ b/tools/gator/daemon/EventsXML.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -13,31 +13,245 @@ #include "OlyUtility.h" #include "SessionData.h" +class XMLList { +public: + XMLList(XMLList *const prev, mxml_node_t *const node) : mPrev(prev), mNode(node) {} + + XMLList *getPrev() { return mPrev; } + mxml_node_t *getNode() const { return mNode; } + void setNode(mxml_node_t *const node) { mNode = node; } + + static void free(XMLList *list) { + while (list != NULL) { + XMLList *prev = list->getPrev(); + delete list; + list = prev; + } + } + +private: + XMLList *const mPrev; + mxml_node_t *mNode; + + // Intentionally unimplemented + XMLList(const XMLList &); + XMLList &operator=(const XMLList &); +}; + mxml_node_t *EventsXML::getTree() { #include "events_xml.h" // defines and initializes char events_xml[] and int events_xml_len char path[PATH_MAX]; - mxml_node_t *xml; + mxml_node_t *xml = NULL; FILE *fl; // Avoid unused variable warning (void)events_xml_len; // Load the provided or default events xml - if (gSessionData->mEventsXMLPath) { - strncpy(path, gSessionData->mEventsXMLPath, PATH_MAX); - } else { - util->getApplicationFullPath(path, PATH_MAX); - strncat(path, "events.xml", PATH_MAX - strlen(path) - 1); + if (gSessionData.mEventsXMLPath) { + strncpy(path, gSessionData.mEventsXMLPath, PATH_MAX); + fl = fopen_cloexec(path, "r"); + if (fl) { + xml = mxmlLoadFile(NULL, fl, MXML_NO_CALLBACK); + if (xml == NULL) { + logg.logError("Unable to parse %s", gSessionData.mEventsXMLPath); + handleException(); + } + fclose(fl); + } } - fl = fopen(path, "r"); - if (fl) { - xml = mxmlLoadFile(NULL, fl, MXML_NO_CALLBACK); - fclose(fl); - } else { - logg->logMessage("Unable to locate events.xml, using default"); + if (xml == NULL) { + logg.logMessage("Unable to locate events.xml, using default"); xml = mxmlLoadString(NULL, (const char *)events_xml, MXML_NO_CALLBACK); } + // Append additional events XML + if (gSessionData.mEventsXMLAppend) { + fl = fopen_cloexec(gSessionData.mEventsXMLAppend, "r"); + if (fl == NULL) { + logg.logError("Unable to open additional events XML %s", gSessionData.mEventsXMLAppend); + handleException(); + } + mxml_node_t *append = mxmlLoadFile(NULL, fl, MXML_NO_CALLBACK); + if (append == NULL) { + logg.logError("Unable to parse %s", gSessionData.mEventsXMLAppend); + handleException(); + } + fclose(fl); + + mxml_node_t *events = mxmlFindElement(xml, xml, "events", NULL, NULL, MXML_DESCEND); + if (!events) { + logg.logError("Unable to find node in the events.xml, please ensure the first two lines of events XML starts with:\n" + "\n" + ""); + handleException(); + } + + XMLList *categoryList = NULL; + XMLList *eventList = NULL; + XMLList *counterSetList = NULL; + { + // Make list of all categories in xml + mxml_node_t *node = xml; + while (true) { + node = mxmlFindElement(node, xml, "category", NULL, NULL, MXML_DESCEND); + if (node == NULL) { + break; + } + categoryList = new XMLList(categoryList, node); + } + + // Make list of all events in xml + node = xml; + while (true) { + node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND); + if (node == NULL) { + break; + } + eventList = new XMLList(eventList, node); + } + + // Make list of all counter_sets in xml + node = xml; + while (true) { + node = mxmlFindElement(node, xml, "counter_set", NULL, NULL, MXML_DESCEND); + if (node == NULL) { + break; + } + counterSetList = new XMLList(counterSetList, node); + } + } + + // Handle events + for (mxml_node_t *node = mxmlFindElement(append, append, "event", NULL, NULL, MXML_DESCEND), + *next = mxmlFindElement(node, append, "event", NULL, NULL, MXML_DESCEND); + node != NULL; + node = next, next = mxmlFindElement(node, append, "event", NULL, NULL, MXML_DESCEND)) { + const char *const category = mxmlElementGetAttr(mxmlGetParent(node), "name"); + const char *const title = mxmlElementGetAttr(node, "title"); + const char *const name = mxmlElementGetAttr(node, "name"); + if (category == NULL || title == NULL || name == NULL) { + logg.logError("Not all event XML nodes have the required title and name and parent name attributes"); + handleException(); + } + + // Replace any duplicate events + for (XMLList *event = eventList; event != NULL; event = event->getPrev()) { + const char *const category2 = mxmlElementGetAttr(mxmlGetParent(event->getNode()), "name"); + const char *const title2 = mxmlElementGetAttr(event->getNode(), "title"); + const char *const name2 = mxmlElementGetAttr(event->getNode(), "name"); + if (category2 == NULL || title2 == NULL || name2 == NULL) { + logg.logError("Not all event XML nodes have the required title and name and parent name attributes"); + handleException(); + } + + if (strcmp(category, category2) == 0 && strcmp(title, title2) == 0 && strcmp(name, name2) == 0) { + logg.logMessage("Replacing counter %s %s: %s", category, title, name); + mxml_node_t *parent = mxmlGetParent(event->getNode()); + mxmlDelete(event->getNode()); + mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node); + event->setNode(node); + break; + } + } + } + + // Handle categories + for (mxml_node_t *node = strcmp(mxmlGetElement(append), "category") == 0 ? append : mxmlFindElement(append, append, "category", NULL, NULL, MXML_DESCEND), + *next = mxmlFindElement(node, append, "category", NULL, NULL, MXML_DESCEND); + node != NULL; + node = next, next = mxmlFindElement(node, append, "category", NULL, NULL, MXML_DESCEND)) { + // After replacing duplicate events, a category may be empty + if (mxmlGetFirstChild(node) == NULL) { + continue; + } + + const char *const name = mxmlElementGetAttr(node, "name"); + if (name == NULL) { + logg.logError("Not all event XML category nodes have the required name attribute"); + handleException(); + } + + // Merge identically named categories + bool merged = false; + for (XMLList *category = categoryList; category != NULL; category = category->getPrev()) { + const char *const name2 = mxmlElementGetAttr(category->getNode(), "name"); + if (name2 == NULL) { + logg.logError("Not all event XML category nodes have the required name attribute"); + handleException(); + } + + if (strcmp(name, name2) == 0) { + logg.logMessage("Merging category %s", name); + while (true) { + mxml_node_t *child = mxmlGetFirstChild(node); + if (child == NULL) { + break; + } + mxmlAdd(category->getNode(), MXML_ADD_AFTER, mxmlGetLastChild(category->getNode()), child); + } + merged = true; + break; + } + } + + if (merged) { + continue; + } + + // Add new categories + logg.logMessage("Appending category %s", name); + mxmlAdd(events, MXML_ADD_AFTER, mxmlGetLastChild(events), node); + } + + // Handle counter_sets + for (mxml_node_t *node = strcmp(mxmlGetElement(append), "counter_set") == 0 ? append : mxmlFindElement(append, append, "counter_set", NULL, NULL, MXML_DESCEND), + *next = mxmlFindElement(node, append, "counter_set", NULL, NULL, MXML_DESCEND); + node != NULL; + node = next, next = mxmlFindElement(node, append, "counter_set", NULL, NULL, MXML_DESCEND)) { + + const char *const name = mxmlElementGetAttr(node, "name"); + if (name == NULL) { + logg.logError("Not all event XML counter_sets have the required name attribute"); + handleException(); + } + + // Replace any duplicate counter_sets + bool replaced = false; + for (XMLList *counterSet = counterSetList; counterSet != NULL; counterSet = counterSet->getPrev()) { + const char *const name2 = mxmlElementGetAttr(counterSet->getNode(), "name"); + if (name2 == NULL) { + logg.logError("Not all event XML nodes have the required title and name and parent name attributes"); + handleException(); + } + + if (strcmp(name, name2) == 0) { + logg.logMessage("Replacing counter %s", name); + mxml_node_t *parent = mxmlGetParent(counterSet->getNode()); + mxmlDelete(counterSet->getNode()); + mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node); + counterSet->setNode(node); + replaced = true; + break; + } + } + + if (replaced) { + continue; + } + + // Add new counter_sets + logg.logMessage("Appending counter_set %s", name); + mxmlAdd(events, MXML_ADD_AFTER, mxmlGetLastChild(events), node); + } + + XMLList::free(eventList); + XMLList::free(categoryList); + XMLList::free(counterSetList); + + mxmlDelete(append); + } + return xml; } @@ -47,7 +261,9 @@ char *EventsXML::getXML() { // Add dynamic events from the drivers mxml_node_t *events = mxmlFindElement(xml, xml, "events", NULL, NULL, MXML_DESCEND); if (!events) { - logg->logError(__FILE__, __LINE__, "Unable to find node in the events.xml"); + logg.logError("Unable to find node in the events.xml, please ensure the first two lines of events XML are:\n" + "\n" + ""); handleException(); } for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { @@ -67,8 +283,8 @@ void EventsXML::write(const char *path) { snprintf(file, PATH_MAX, "%s/events.xml", path); char *buf = getXML(); - if (util->writeToDisk(file, buf) < 0) { - logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file); + if (writeToDisk(file, buf) < 0) { + logg.logError("Error writing %s\nPlease verify the path.", file); handleException(); } diff --git a/tools/gator/daemon/EventsXML.h b/tools/gator/daemon/EventsXML.h index ff7a02f..2b38fa4 100644 --- a/tools/gator/daemon/EventsXML.h +++ b/tools/gator/daemon/EventsXML.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -13,9 +13,16 @@ class EventsXML { public: + EventsXML() {} + mxml_node_t *getTree(); char *getXML(); void write(const char* path); + +private: + // Intentionally unimplemented + EventsXML(const EventsXML &); + EventsXML &operator=(const EventsXML &); }; #endif // EVENTS_XML diff --git a/tools/gator/daemon/ExternalDriver.cpp b/tools/gator/daemon/ExternalDriver.cpp new file mode 100644 index 0000000..a7fbaff --- /dev/null +++ b/tools/gator/daemon/ExternalDriver.cpp @@ -0,0 +1,269 @@ +/** + * Copyright (C) ARM Limited 2010-2015. 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 "ExternalDriver.h" + +#include +#include + +#include "Buffer.h" +#include "Logging.h" +#include "OlySocket.h" +#include "SessionData.h" + +static const char MALI_UTGARD_SETUP[] = "\0mali-utgard-setup"; +static const char SETUP_VERSION[] = "ANNOTATE_SETUP 1\n"; +static const size_t HEADER_SIZE = 1 + sizeof(uint32_t); + +#define HEADER_ERROR 0x80 +#define HEADER_ACK 0x81 +#define HEADER_REQUEST_COUNTERS 0x82 +#define HEADER_COUNTERS 0x83 +#define HEADER_ENABLE_COUNTERS 0x84 +#define HEADER_START 0x85 + +static uint32_t readLEInt(char *const buf) +{ + size_t i; + uint32_t v; + + v = 0; + for (i = 0; i < sizeof(v); ++i) + v |= (uint32_t)buf[i] << 8*i; + + return v; +} + +static int readPackedInt(char *const buf, const size_t bufSize, size_t *const pos, uint64_t *const l) +{ + uint8_t shift = 0; + uint8_t b = ~0; + + *l = 0; + while ((b & 0x80) != 0) { + if (*pos >= bufSize) { + return -1; + } + b = buf[*pos]; + *pos += 1; + *l |= (uint64_t)(b & 0x7f) << shift; + shift += 7; + } + + if (shift < 8*sizeof(*l) && (b & 0x40) != 0) { + *l |= -(1 << shift); + } + + return 0; +} + +class ExternalCounter : public DriverCounter { +public: + ExternalCounter(DriverCounter *next, const char *name, int cores) : DriverCounter(next, name), mCores(cores), mEvent(-1) {} + + ~ExternalCounter() { + } + + int getCores() const { return mCores; } + void setEvent(const int event) { mEvent = event; } + int getEvent() const { return mEvent; } + +private: + const int mCores; + int mEvent; + + // Intentionally undefined + ExternalCounter(const ExternalCounter &); + ExternalCounter &operator=(const ExternalCounter &); +}; + +ExternalDriver::ExternalDriver() : mUds(-1), mQueried(false), mStarted(false) { +} + +bool ExternalDriver::connect() const { + if (mUds < 0) { + mUds = OlySocket::connect(MALI_UTGARD_SETUP, sizeof(MALI_UTGARD_SETUP)); + if (mUds >= 0 && !writeAll(mUds, SETUP_VERSION, sizeof(SETUP_VERSION) - 1)) { + logg.logError("Unable to send setup version"); + handleException(); + } + } + return mUds >= 0; +} + +void ExternalDriver::disconnect() { + if (mUds >= 0) { + close(mUds); + mUds = -1; + mStarted = false; + } +} + +void ExternalDriver::query() const { + if (mQueried) { + return; + } + // Only try once even if it fails otherwise not all the possible counters may be shown + mQueried = true; + + char *const buf = gSessionData.mSharedData->mMaliUtgardCounters; + const size_t bufSize = sizeof(gSessionData.mSharedData->mMaliUtgardCounters); + size_t size = 0; + + if (!connect()) { + size = gSessionData.mSharedData->mMaliUtgardCountersSize; + logg.logMessage("Unable to connect, using cached version; size: %zi", size); + } else { + gSessionData.mSharedData->mMaliUtgardCountersSize = 0; + + buf[0] = HEADER_REQUEST_COUNTERS; + size_t pos = HEADER_SIZE; + Buffer::writeLEInt((unsigned char *)(buf + 1), pos); + if (!writeAll(mUds, buf, pos)) { + logg.logError("Unable to send request counters message"); + handleException(); + } + + if (!readAll(mUds, buf, HEADER_SIZE) || buf[0] != (char)HEADER_COUNTERS) { + logg.logError("Unable to read request counters response header"); + handleException(); + } + size = readLEInt(buf + 1); + if (size > bufSize || !readAll(mUds, buf, size - HEADER_SIZE)) { + logg.logError("Unable to read request counters response"); + handleException(); + } + + size -= HEADER_SIZE; + gSessionData.mSharedData->mMaliUtgardCountersSize = size; + logg.logMessage("Requested counters; size: %zi", size); + } + + size_t pos = 0; + while (pos < size) { + size_t begin = pos; + char *name = NULL; + uint64_t cores = -1; + while (pos < size && buf[pos] != '\0') { + ++pos; + } + if (pos > begin) { + name = strndup(buf + begin, pos - begin); + } + if (pos < size && buf[pos] == '\0') { + ++pos; + } + ; + if (name != NULL && readPackedInt(buf, bufSize, &pos, &cores) == 0) { + // Cheat so that this can be 'const' + ((ExternalDriver *)(this))->setCounters(new ExternalCounter(getCounters(), name, cores)); + } + } + + if (pos != size) { + logg.logError("Unable to parse request counters response"); + handleException(); + } +} + +void ExternalDriver::start() { + if (!connect()) { + return; + } + + if (mStarted) { + return; + } + // Only start once + mStarted = true; + + char buf[1<<12]; + int pos; + + buf[0] = HEADER_ENABLE_COUNTERS; + pos = HEADER_SIZE; + for (ExternalCounter *counter = static_cast(getCounters()); counter != NULL; counter = static_cast(counter->getNext())) { + if (!counter->isEnabled()) { + continue; + } + size_t nameLen = strlen(counter->getName()); + if (pos + nameLen + 1 + 2*Buffer::MAXSIZE_PACK32 > sizeof(buf)) { + logg.logError("Unable to enable counters, message is too large"); + handleException(); + } + memcpy(buf + pos, counter->getName(), nameLen + 1); + pos += nameLen + 1; + Buffer::packInt(buf, sizeof(buf), pos, counter->getEvent()); + Buffer::packInt(buf, sizeof(buf), pos, counter->getKey()); + } + Buffer::writeLEInt((unsigned char *)(buf + 1), pos); + if (!writeAll(mUds, buf, pos)) { + logg.logError("Unable to send enable counters message"); + handleException(); + } + + size_t size = 0; + if (!readAll(mUds, buf, HEADER_SIZE) || buf[0] != (char)HEADER_ACK) { + logg.logError("Unable to read enable counters response header"); + handleException(); + } + size = readLEInt(buf + 1); + if (size != HEADER_SIZE) { + logg.logError("Unable to parse enable counters response"); + handleException(); + } + + buf[0] = HEADER_START; + pos = HEADER_SIZE; + // ns/sec / samples/sec = ns/sample + // For sample rate of none, sample every 100ms + Buffer::packInt(buf, sizeof(buf), pos, NS_PER_S / (gSessionData.mSampleRate == 0 ? 10 : gSessionData.mSampleRate)); + Buffer::packInt(buf, sizeof(buf), pos, gSessionData.mLiveRate); + Buffer::writeLEInt((unsigned char *)(buf + 1), pos); + if (!writeAll(mUds, buf, pos)) { + logg.logError("Unable to send start message"); + handleException(); + } + + size = 0; + if (!readAll(mUds, buf, HEADER_SIZE) || buf[0] != (char)HEADER_ACK) { + logg.logError("Unable to read start response header"); + handleException(); + } + size = readLEInt(buf + 1); + if (size != HEADER_SIZE) { + logg.logError("Unable to parse start response"); + handleException(); + } +} + +bool ExternalDriver::claimCounter(const Counter &counter) const { + query(); + return super::claimCounter(counter); +} + +void ExternalDriver::setupCounter(Counter &counter) { + ExternalCounter *const externalCounter = static_cast(findCounter(counter)); + if (externalCounter == NULL) { + counter.setEnabled(false); + return; + } + externalCounter->setEnabled(true); + counter.setKey(externalCounter->getKey()); + if (counter.getEvent() != -1) { + externalCounter->setEvent(counter.getEvent()); + } + if (externalCounter->getCores() > 0) { + counter.setCores(externalCounter->getCores()); + } +} + +void ExternalDriver::resetCounters() { + query(); + super::resetCounters(); +} diff --git a/tools/gator/daemon/ExternalDriver.h b/tools/gator/daemon/ExternalDriver.h new file mode 100644 index 0000000..d88f9e1 --- /dev/null +++ b/tools/gator/daemon/ExternalDriver.h @@ -0,0 +1,41 @@ +/** + * Copyright (C) ARM Limited 2010-2015. 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. + */ + +#ifndef EXTERNALDRIVER_H +#define EXTERNALDRIVER_H + +#include "Driver.h" + +class ExternalDriver : public SimpleDriver { +public: + ExternalDriver(); + + bool claimCounter(const Counter &counter) const; + void resetCounters(); + void setupCounter(Counter &counter); + + void start(); + + void disconnect(); + +private: + typedef SimpleDriver super; + + bool connect() const; + void query() const; + + mutable int mUds; + mutable bool mQueried; + bool mStarted; + + // Intentionally unimplemented + ExternalDriver(const ExternalDriver &); + ExternalDriver &operator=(const ExternalDriver &); +}; + +#endif // EXTERNALDRIVER_H diff --git a/tools/gator/daemon/ExternalSource.cpp b/tools/gator/daemon/ExternalSource.cpp index 8f5e6b6..c6626ce 100644 --- a/tools/gator/daemon/ExternalSource.cpp +++ b/tools/gator/daemon/ExternalSource.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -12,35 +12,25 @@ #include #include +#include "Child.h" +#include "DriverSource.h" #include "Logging.h" #include "OlySocket.h" #include "SessionData.h" +extern Child *child; + +static const char STREAMLINE_ANNOTATE[] = "\0streamline-annotate"; static const char MALI_VIDEO[] = "\0mali-video"; static const char MALI_VIDEO_STARTUP[] = "\0mali-video-startup"; static const char MALI_VIDEO_V1[] = "MALI_VIDEO 1\n"; -static const char MALI_GRAPHICS[] = "\0mali_thirdparty_server"; static const char MALI_GRAPHICS_STARTUP[] = "\0mali_thirdparty_client"; static const char MALI_GRAPHICS_V1[] = "MALI_GRAPHICS 1\n"; +static const char MALI_UTGARD_STARTUP[] = "\0mali-utgard-startup"; +static const char FTRACE_V1[] = "FTRACE 1\n"; +static const char FTRACE_V2[] = "FTRACE 2\n"; -static bool setNonblock(const int fd) { - int flags; - - flags = fcntl(fd, F_GETFL); - if (flags < 0) { - logg->logMessage("fcntl getfl failed"); - return false; - } - - if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) { - logg->logMessage("fcntl setfl failed"); - return false; - } - - return true; -} - -ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 128*1024, senderSem), mMonitor(), mMveStartupUds(MALI_VIDEO_STARTUP, sizeof(MALI_VIDEO_STARTUP)), mMaliStartupUds(MALI_GRAPHICS_STARTUP, sizeof(MALI_GRAPHICS_STARTUP)), mAnnotate(8083), mInterruptFd(-1), mMaliUds(-1), mMveUds(-1) { +ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 128*1024, senderSem), mMonitor(), mMveStartupUds(MALI_VIDEO_STARTUP, sizeof(MALI_VIDEO_STARTUP)), mMidgardStartupUds(MALI_GRAPHICS_STARTUP, sizeof(MALI_GRAPHICS_STARTUP)), mUtgardStartupUds(MALI_UTGARD_STARTUP, sizeof(MALI_UTGARD_STARTUP)), mAnnotate(8083), mAnnotateUds(STREAMLINE_ANNOTATE, sizeof(STREAMLINE_ANNOTATE), true), mInterruptFd(-1), mMidgardUds(-1), mMveUds(-1) { sem_init(&mBufferSem, 0, 0); } @@ -49,18 +39,22 @@ ExternalSource::~ExternalSource() { void ExternalSource::waitFor(const int bytes) { while (mBuffer.bytesAvailable() <= bytes) { + if (gSessionData.mOneShot && gSessionData.mSessionIsActive) { + logg.logMessage("One shot (external)"); + child->endSession(); + } sem_wait(&mBufferSem); } } void ExternalSource::configureConnection(const int fd, const char *const handshake, size_t size) { if (!setNonblock(fd)) { - logg->logError(__FILE__, __LINE__, "Unable to set nonblock on fh"); + logg.logError("Unable to set nonblock on fh"); handleException(); } if (!mMonitor.add(fd)) { - logg->logError(__FILE__, __LINE__, "Unable to add fh to monitor"); + logg.logError("Unable to add fh to monitor"); handleException(); } @@ -68,22 +62,26 @@ void ExternalSource::configureConnection(const int fd, const char *const handsha waitFor(Buffer::MAXSIZE_PACK32 + size - 1); mBuffer.packInt(fd); mBuffer.writeBytes(handshake, size - 1); - mBuffer.commit(1); + mBuffer.commit(1, true); } -bool ExternalSource::connectMali() { - mMaliUds = OlySocket::connect(MALI_GRAPHICS, sizeof(MALI_GRAPHICS)); - if (mMaliUds < 0) { +bool ExternalSource::connectMidgard() { + mMidgardUds = OlySocket::connect(MALI_GRAPHICS, MALI_GRAPHICS_SIZE); + if (mMidgardUds < 0) { + return false; + } + + if (!gSessionData.mMidgard.start(mMidgardUds)) { return false; } - configureConnection(mMaliUds, MALI_GRAPHICS_V1, sizeof(MALI_GRAPHICS_V1)); + configureConnection(mMidgardUds, MALI_GRAPHICS_V1, sizeof(MALI_GRAPHICS_V1)); return true; } bool ExternalSource::connectMve() { - if (!gSessionData->maliVideo.countersEnabled()) { + if (!gSessionData.mMaliVideo.countersEnabled()) { return true; } @@ -92,7 +90,7 @@ bool ExternalSource::connectMve() { return false; } - if (!gSessionData->maliVideo.start(mMveUds)) { + if (!gSessionData.mMaliVideo.start(mMveUds)) { return false; } @@ -101,17 +99,42 @@ bool ExternalSource::connectMve() { return true; } +void ExternalSource::connectFtrace() { + if (!gSessionData.mFtraceDriver.isSupported()) { + return; + } + + int ftraceFds[NR_CPUS + 1]; + const char *handshake; + size_t size; + if (gSessionData.mFtraceDriver.prepare(ftraceFds)) { + handshake = FTRACE_V1; + size = sizeof(FTRACE_V1); + } else { + handshake = FTRACE_V2; + size = sizeof(FTRACE_V2); + } + + for (int i = 0; i < ARRAY_LENGTH(ftraceFds) && ftraceFds[i] >= 0; ++i) { + configureConnection(ftraceFds[i], handshake, size); + } +} + bool ExternalSource::prepare() { if (!mMonitor.init() || !setNonblock(mMveStartupUds.getFd()) || !mMonitor.add(mMveStartupUds.getFd()) || - !setNonblock(mMaliStartupUds.getFd()) || !mMonitor.add(mMaliStartupUds.getFd()) || + !setNonblock(mMidgardStartupUds.getFd()) || !mMonitor.add(mMidgardStartupUds.getFd()) || + !setNonblock(mUtgardStartupUds.getFd()) || !mMonitor.add(mUtgardStartupUds.getFd()) || !setNonblock(mAnnotate.getFd()) || !mMonitor.add(mAnnotate.getFd()) || + !setNonblock(mAnnotateUds.getFd()) || !mMonitor.add(mAnnotateUds.getFd()) || false) { return false; } - connectMali(); + connectMidgard(); connectMve(); + connectFtrace(); + gSessionData.mExternalDriver.start(); return true; } @@ -122,30 +145,54 @@ void ExternalSource::run() { prctl(PR_SET_NAME, (unsigned long)&"gatord-external", 0, 0, 0); if (pipe_cloexec(pipefd) != 0) { - logg->logError(__FILE__, __LINE__, "pipe failed"); + logg.logError("pipe failed"); handleException(); } mInterruptFd = pipefd[1]; if (!mMonitor.add(pipefd[0])) { - logg->logError(__FILE__, __LINE__, "Monitor::add failed"); + logg.logError("Monitor::add failed"); handleException(); } // Notify annotate clients to retry connecting to gatord - gSessionData->annotateListener.signal(); + uint64_t val = 1; + if (::write(gSessionData.mAnnotateStart, &val, sizeof(val)) != sizeof(val)) { + logg.logMessage("Writing to annotate pipe failed"); + } + + if (gSessionData.mFtraceDriver.isSupported()) { + gSessionData.mAtraceDriver.start(); + gSessionData.mTtraceDriver.start(); + gSessionData.mFtraceDriver.start(); + } - while (gSessionData->mSessionIsActive) { + // Wait until monotonicStarted is set before sending data + int64_t monotonicStarted = 0; + while (monotonicStarted <= 0 && gSessionData.mSessionIsActive) { + usleep(10); + + if (gSessionData.mPerf.isSetup()) { + monotonicStarted = gSessionData.mMonotonicStarted; + } else { + if (DriverSource::readInt64Driver("/dev/gator/started", &monotonicStarted) == -1) { + logg.logError("Error reading gator driver start time"); + handleException(); + } + } + } + + while (gSessionData.mSessionIsActive) { struct epoll_event events[16]; // Clear any pending sem posts while (sem_trywait(&mBufferSem) == 0); int ready = mMonitor.wait(events, ARRAY_LENGTH(events), -1); if (ready < 0) { - logg->logError(__FILE__, __LINE__, "Monitor::wait failed"); + logg.logError("Monitor::wait failed"); handleException(); } - const uint64_t currTime = getTime(); + const uint64_t currTime = getTime() - gSessionData.mMonotonicStarted; for (int i = 0; i < ready; ++i) { const int fd = events[i].data.fd; @@ -155,22 +202,35 @@ void ExternalSource::run() { // Don't read from this connection, establish a new connection to Mali-V500 close(client); if (!connectMve()) { - logg->logError(__FILE__, __LINE__, "Unable to configure incoming Mali video connection"); + logg.logError("Unable to configure incoming Mali video connection"); handleException(); } - } else if (fd == mMaliStartupUds.getFd()) { - // Mali Graphics says it's alive - int client = mMaliStartupUds.acceptConnection(); - // Don't read from this connection, establish a new connection to Mali Graphics + } else if (fd == mMidgardStartupUds.getFd()) { + // Midgard says it's alive + int client = mMidgardStartupUds.acceptConnection(); + // Don't read from this connection, establish a new connection to Midgard close(client); - if (!connectMali()) { - logg->logError(__FILE__, __LINE__, "Unable to configure incoming Mali graphics connection"); + if (!connectMidgard()) { + logg.logError("Unable to configure incoming Midgard graphics connection"); handleException(); } + } else if (fd == mUtgardStartupUds.getFd()) { + // Mali Utgard says it's alive + int client = mUtgardStartupUds.acceptConnection(); + // Don't read from this connection, configure utgard and expect them to reconnect with annotations + close(client); + gSessionData.mExternalDriver.disconnect(); + gSessionData.mExternalDriver.start(); } else if (fd == mAnnotate.getFd()) { int client = mAnnotate.acceptConnection(); if (!setNonblock(client) || !mMonitor.add(client)) { - logg->logError(__FILE__, __LINE__, "Unable to set socket options on incoming annotation connection"); + logg.logError("Unable to set socket options on incoming annotation connection"); + handleException(); + } + } else if (fd == mAnnotateUds.getFd()) { + int client = mAnnotateUds.acceptConnection(); + if (!setNonblock(client) || !mMonitor.add(client)) { + logg.logError("Unable to set socket options on incoming annotation connection"); handleException(); } } else if (fd == pipefd[0]) { @@ -181,40 +241,8 @@ void ExternalSource::run() { * recommended that threads annotate that much as it can also * starve out the gator data. */ - while (gSessionData->mSessionIsActive) { - // Wait until there is enough room for the fd, two headers and two ints - waitFor(7*Buffer::MAXSIZE_PACK32 + 2*sizeof(uint32_t)); - mBuffer.packInt(fd); - const int contiguous = mBuffer.contiguousSpaceAvailable(); - const int bytes = read(fd, mBuffer.getWritePos(), contiguous); - if (bytes < 0) { - if (errno == EAGAIN) { - // Nothing left to read - mBuffer.commit(currTime); - break; - } - // Something else failed, close the socket - mBuffer.commit(currTime); - mBuffer.packInt(-1); - mBuffer.packInt(fd); - mBuffer.commit(currTime); - close(fd); - break; - } else if (bytes == 0) { - // The other side is closed - mBuffer.commit(currTime); - mBuffer.packInt(-1); - mBuffer.packInt(fd); - mBuffer.commit(currTime); - close(fd); - break; - } - - mBuffer.advanceWrite(bytes); - mBuffer.commit(currTime); - - // Short reads also mean nothing is left to read - if (bytes < contiguous) { + while (gSessionData.mSessionIsActive) { + if (!transfer(currTime, fd)) { break; } } @@ -222,10 +250,23 @@ void ExternalSource::run() { } } + if (gSessionData.mFtraceDriver.isSupported()) { + int ftraceFds[NR_CPUS + 1]; + gSessionData.mFtraceDriver.stop(ftraceFds); + // Read any slop + const uint64_t currTime = getTime() - gSessionData.mMonotonicStarted; + for (int i = 0; i < ARRAY_LENGTH(ftraceFds) && ftraceFds[i] >= 0; ++i) { + transfer(currTime, ftraceFds[i]); + close(ftraceFds[i]); + } + gSessionData.mTtraceDriver.stop(); + gSessionData.mAtraceDriver.stop(); + } + mBuffer.setDone(); if (mMveUds >= 0) { - gSessionData->maliVideo.stop(mMveUds); + gSessionData.mMaliVideo.stop(mMveUds); } mInterruptFd = -1; @@ -233,12 +274,49 @@ void ExternalSource::run() { close(pipefd[1]); } +bool ExternalSource::transfer(const uint64_t currTime, const int fd) { + // Wait until there is enough room for the fd, two headers and two ints + waitFor(7*Buffer::MAXSIZE_PACK32 + 2*sizeof(uint32_t)); + mBuffer.packInt(fd); + const int contiguous = mBuffer.contiguousSpaceAvailable(); + const int bytes = read(fd, mBuffer.getWritePos(), contiguous); + if (bytes < 0) { + if (errno == EAGAIN) { + // Nothing left to read + mBuffer.commit(currTime, true); + return false; + } + // Something else failed, close the socket + mBuffer.commit(currTime, true); + mBuffer.packInt(-1); + mBuffer.packInt(fd); + // Here and other commits, always force-flush the buffer as this frame don't work like others + mBuffer.commit(currTime, true); + close(fd); + return false; + } else if (bytes == 0) { + // The other side is closed + mBuffer.commit(currTime, true); + mBuffer.packInt(-1); + mBuffer.packInt(fd); + mBuffer.commit(currTime, true); + close(fd); + return false; + } + + mBuffer.advanceWrite(bytes); + mBuffer.commit(currTime, true); + + // Short reads also mean nothing is left to read + return bytes >= contiguous; +} + void ExternalSource::interrupt() { if (mInterruptFd >= 0) { int8_t c = 0; // Write to the pipe to wake the monitor which will cause mSessionIsActive to be reread if (::write(mInterruptFd, &c, sizeof(c)) != sizeof(c)) { - logg->logError(__FILE__, __LINE__, "write failed"); + logg.logError("write failed"); handleException(); } } @@ -250,7 +328,7 @@ bool ExternalSource::isDone() { void ExternalSource::write(Sender *sender) { // Don't send external data until the summary packet is sent so that monotonic delta is available - if (!gSessionData->mSentSummary) { + if (!gSessionData.mSentSummary) { return; } if (!mBuffer.isDone()) { diff --git a/tools/gator/daemon/ExternalSource.h b/tools/gator/daemon/ExternalSource.h index 919e75e..210be63 100644 --- a/tools/gator/daemon/ExternalSource.h +++ b/tools/gator/daemon/ExternalSource.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -32,17 +32,21 @@ public: private: void waitFor(const int bytes); void configureConnection(const int fd, const char *const handshake, size_t size); - bool connectMali(); + bool connectMidgard(); bool connectMve(); + void connectFtrace(); + bool transfer(const uint64_t currTime, const int fd); sem_t mBufferSem; Buffer mBuffer; Monitor mMonitor; OlyServerSocket mMveStartupUds; - OlyServerSocket mMaliStartupUds; + OlyServerSocket mMidgardStartupUds; + OlyServerSocket mUtgardStartupUds; OlyServerSocket mAnnotate; + OlyServerSocket mAnnotateUds; int mInterruptFd; - int mMaliUds; + int mMidgardUds; int mMveUds; // Intentionally unimplemented diff --git a/tools/gator/daemon/FSDriver.cpp b/tools/gator/daemon/FSDriver.cpp index dd8eb80..bd4ef47 100644 --- a/tools/gator/daemon/FSDriver.cpp +++ b/tools/gator/daemon/FSDriver.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2014. All rights reserved. + * Copyright (C) ARM Limited 2014-2015. 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 @@ -42,7 +42,7 @@ FSCounter::FSCounter(DriverCounter *next, char *name, char *path, const char *re if (result != 0) { char buf[128]; regerror(result, &mReg, buf, sizeof(buf)); - logg->logError(__FILE__, __LINE__, "Invalid regex '%s': %s", regex, buf); + logg.logError("Invalid regex '%s': %s", regex, buf); handleException(); } } @@ -79,21 +79,19 @@ int64_t FSCounter::read() { regmatch_t match[2]; int result = regexec(&mReg, buf, 2, match, 0); if (result != 0) { - regerror(result, &mReg, buf, sizeof(buf)); - logg->logError(__FILE__, __LINE__, "Parsing %s failed: %s", mPath, buf); - handleException(); + // No match + return 0; } if (match[1].rm_so < 0) { - logg->logError(__FILE__, __LINE__, "Parsing %s failed", mPath); - handleException(); - } - - errno = 0; - value = strtoll(buf + match[1].rm_so, NULL, 0); - if (errno != 0) { - logg->logError(__FILE__, __LINE__, "Parsing %s failed: %s", mPath, strerror(errno)); - handleException(); + value = 1; + } else { + errno = 0; + value = strtoll(buf + match[1].rm_so, NULL, 0); + if (errno != 0) { + logg.logError("Parsing %s failed: %s", mPath, strerror(errno)); + handleException(); + } } } else { if (DriverSource::readInt64Driver(mPath, &value) != 0) { @@ -103,7 +101,7 @@ int64_t FSCounter::read() { return value; fail: - logg->logError(__FILE__, __LINE__, "Unable to read %s", mPath); + logg.logError("Unable to read %s", mPath); handleException(); } @@ -126,7 +124,7 @@ void FSDriver::readEvents(mxml_node_t *const xml) { } if (counter[0] == '/') { - logg->logError(__FILE__, __LINE__, "Old style filesystem counter (%s) detected, please create a new unique counter value and move the filename into the path attribute, see events-Filesystem.xml for examples", counter); + logg.logError("Old style filesystem counter (%s) detected, please create a new unique counter value and move the filename into the path attribute, see events-Filesystem.xml for examples", counter); handleException(); } @@ -136,7 +134,7 @@ void FSDriver::readEvents(mxml_node_t *const xml) { const char *path = mxmlElementGetAttr(node, "path"); if (path == NULL) { - logg->logError(__FILE__, __LINE__, "The filesystem counter %s is missing the required path attribute", counter); + logg.logError("The filesystem counter %s is missing the required path attribute", counter); handleException(); } const char *regex = mxmlElementGetAttr(node, "regex"); diff --git a/tools/gator/daemon/FSDriver.h b/tools/gator/daemon/FSDriver.h index a7dc8b4..63a4e90 100644 --- a/tools/gator/daemon/FSDriver.h +++ b/tools/gator/daemon/FSDriver.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2014. All rights reserved. + * Copyright (C) ARM Limited 2014-2015. 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 diff --git a/tools/gator/daemon/Fifo.cpp b/tools/gator/daemon/Fifo.cpp index 41275fd..560ace4 100644 --- a/tools/gator/daemon/Fifo.cpp +++ b/tools/gator/daemon/Fifo.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -24,12 +24,12 @@ Fifo::Fifo(int singleBufferSize, int bufferSize, sem_t* readerSem) { mEnd = false; if (mBuffer == NULL) { - logg->logError(__FILE__, __LINE__, "failed to allocate %d bytes", bufferSize + singleBufferSize); + logg.logError("failed to allocate %d bytes", bufferSize + singleBufferSize); handleException(); } if (sem_init(&mWaitForSpaceSem, 0, 0)) { - logg->logError(__FILE__, __LINE__, "sem_init() failed"); + logg.logError("sem_init() failed"); handleException(); } } diff --git a/tools/gator/daemon/Fifo.h b/tools/gator/daemon/Fifo.h index 21c8d85..01fa11b 100644 --- a/tools/gator/daemon/Fifo.h +++ b/tools/gator/daemon/Fifo.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 diff --git a/tools/gator/daemon/FtraceDriver.cpp b/tools/gator/daemon/FtraceDriver.cpp index b156f1c..4ea3bf1 100644 --- a/tools/gator/daemon/FtraceDriver.cpp +++ b/tools/gator/daemon/FtraceDriver.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2014. All rights reserved. + * Copyright (C) ARM Limited 2014-2015. 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 @@ -8,66 +8,278 @@ #include "FtraceDriver.h" +#include +#include #include +#include +#include +#include +#include +#include +#include "Buffer.h" +#include "Config.h" +#include "DriverSource.h" #include "Logging.h" +#include "Proc.h" +#include "SessionData.h" + +Barrier::Barrier() : mCount(0) { + pthread_mutex_init(&mMutex, NULL); + pthread_cond_init(&mCond, NULL); +} + +Barrier::~Barrier() { + pthread_cond_destroy(&mCond); + pthread_mutex_destroy(&mMutex); +} + +void Barrier::init(unsigned int count) { + mCount = count; +} + +void Barrier::wait() { + pthread_mutex_lock(&mMutex); + + mCount--; + if (mCount == 0) { + pthread_cond_broadcast(&mCond); + } else { + // Loop in case of spurious wakeups + for (;;) { + pthread_cond_wait(&mCond, &mMutex); + if (mCount <= 0) { + break; + } + } + } + + pthread_mutex_unlock(&mMutex); +} class FtraceCounter : public DriverCounter { public: - FtraceCounter(DriverCounter *next, char *name, const char *regex); + FtraceCounter(DriverCounter *next, char *name, const char *enable); ~FtraceCounter(); - int read(const char *const line, int64_t *values); + bool readTracepointFormat(const uint64_t currTime, Buffer *const buffer, DynBuf *const printb, DynBuf *const b); + + void prepare(); + void stop(); private: - regex_t reg; + char *const mEnable; + int mWasEnabled; // Intentionally unimplemented FtraceCounter(const FtraceCounter &); FtraceCounter &operator=(const FtraceCounter &); }; -FtraceCounter::FtraceCounter(DriverCounter *next, char *name, const char *regex) : DriverCounter(next, name) { - int result = regcomp(®, regex, REG_EXTENDED); - if (result != 0) { - char buf[128]; - regerror(result, ®, buf, sizeof(buf)); - logg->logError(__FILE__, __LINE__, "Invalid regex '%s': %s", regex, buf); +FtraceCounter::FtraceCounter(DriverCounter *next, char *name, const char *enable) : DriverCounter(next, name), mEnable(enable == NULL ? NULL : strdup(enable)) { +} + +FtraceCounter::~FtraceCounter() { + if (mEnable != NULL) { + free(mEnable); + } +} + +void FtraceCounter::prepare() { + if (mEnable == NULL) { + if (gSessionData.mFtraceRaw) { + logg.logError("The ftrace counter %s is not compatible with the more efficient ftrace collection as it is missing the enable attribute. Please either add the enable attribute to the counter in events XML or disable the counter in counter configuration.", getName()); + handleException(); + } + return; + } + + char buf[1<<10]; + snprintf(buf, sizeof(buf), EVENTS_PATH "/%s/enable", mEnable); + if ((DriverSource::readIntDriver(buf, &mWasEnabled) != 0) || + (DriverSource::writeDriver(buf, 1) != 0)) { + logg.logError("Unable to read or write to %s", buf); handleException(); } } -FtraceCounter::~FtraceCounter() { - regfree(®); +void FtraceCounter::stop() { + if (mEnable == NULL) { + return; + } + + char buf[1<<10]; + snprintf(buf, sizeof(buf), EVENTS_PATH "/%s/enable", mEnable); + DriverSource::writeDriver(buf, mWasEnabled); +} + +bool FtraceCounter::readTracepointFormat(const uint64_t currTime, Buffer *const buffer, DynBuf *const printb, DynBuf *const b) { + return ::readTracepointFormat(currTime, buffer, mEnable, printb, b); +} + +static void handlerUsr1(int signum) +{ + (void)signum; + + // Although this signal handler does nothing, SIG_IGN doesn't interrupt splice in all cases } -int FtraceCounter::read(const char *const line, int64_t *values) { - regmatch_t match[2]; - int result = regexec(®, line, 2, match, 0); - if (result != 0) { - // No match - return 0; +static int pageSize; + +class FtraceReader { +public: + FtraceReader(Barrier *const barrier, int cpu, int tfd, int pfd0, int pfd1) : mNext(mHead), mBarrier(barrier), mCpu(cpu), mTfd(tfd), mPfd0(pfd0), mPfd1(pfd1) { + mHead = this; } - if (match[1].rm_so < 0) { - logg->logError(__FILE__, __LINE__, "Parsing %s failed", getName()); + void start(); + bool interrupt(); + bool join(); + + static FtraceReader *getHead() { return mHead; } + FtraceReader *getNext() const { return mNext; } + int getPfd0() const { return mPfd0; } + +private: + static FtraceReader *mHead; + FtraceReader *const mNext; + Barrier *const mBarrier; + pthread_t mThread; + const int mCpu; + const int mTfd; + const int mPfd0; + const int mPfd1; + + static void *runStatic(void *arg); + void run(); +}; + +FtraceReader *FtraceReader::mHead; + +void FtraceReader::start() { + if (pthread_create(&mThread, NULL, runStatic, this) != 0) { + logg.logError("Unable to start the ftraceReader thread"); handleException(); } +} - errno = 0; - int64_t value = strtoll(line + match[1].rm_so, NULL, 0); - if (errno != 0) { - logg->logError(__FILE__, __LINE__, "Parsing %s failed: %s", getName(), strerror(errno)); +bool FtraceReader::interrupt() { + return pthread_kill(mThread, SIGUSR1) == 0; +} + +bool FtraceReader::join() { + return pthread_join(mThread, NULL) == 0; +} + +void *FtraceReader::runStatic(void *arg) { + FtraceReader *const ftraceReader = static_cast(arg); + ftraceReader->run(); + return NULL; +} + +#ifndef SPLICE_F_MOVE + +#include + +// Pre Android-21 does not define splice +#define SPLICE_F_MOVE 1 + +static ssize_t sys_splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags) { + return syscall(__NR_splice, fd_in, off_in, fd_out, off_out, len, flags); +} + +#define splice(fd_in, off_in, fd_out, off_out, len, flags) sys_splice(fd_in, off_in, fd_out, off_out, len, flags) + +#endif + +void FtraceReader::run() { + { + char buf[16]; + snprintf(buf, sizeof(buf), "gatord-reader%02i", mCpu); + prctl(PR_SET_NAME, (unsigned long)&buf, 0, 0, 0); + } + + mBarrier->wait(); + + while (gSessionData.mSessionIsActive) { + const ssize_t bytes = splice(mTfd, NULL, mPfd1, NULL, pageSize, SPLICE_F_MOVE); + if (bytes == 0) { + logg.logError("ftrace splice unexpectedly returned 0"); + handleException(); + } else if (bytes < 0) { + if (errno != EINTR) { + logg.logError("splice failed"); + handleException(); + } + } else { + // Can there be a short splice read? + if (bytes != pageSize) { + logg.logError("splice short read"); + handleException(); + } + // Will be read by gatord-external + } + } + + if (!setNonblock(mTfd)) { + logg.logError("setNonblock failed"); handleException(); } - values[0] = getKey(); - values[1] = value; + for (;;) { + ssize_t bytes; - return 1; + bytes = splice(mTfd, NULL, mPfd1, NULL, pageSize, SPLICE_F_MOVE); + if (bytes <= 0) { + break; + } else { + // Can there be a short splice read? + if (bytes != pageSize) { + logg.logError("splice short read"); + handleException(); + } + // Will be read by gatord-external + } + } + + { + // Read any slop + ssize_t bytes; + size_t size; + char buf[1<<16]; + + if (sizeof(buf) < (size_t)pageSize) { + logg.logError("ftrace slop buffer is too small"); + handleException(); + } + for (;;) { + bytes = read(mTfd, buf, sizeof(buf)); + if (bytes == 0) { + logg.logError("ftrace read unexpectedly returned 0"); + handleException(); + } else if (bytes < 0) { + if (errno != EAGAIN) { + logg.logError("reading slop from ftrace failed"); + handleException(); + } + break; + } else { + size = bytes; + bytes = write(mPfd1, buf, size); + if (bytes != (ssize_t)size) { + logg.logError("writing slop to ftrace pipe failed"); + handleException(); + } + } + } + } + + close(mTfd); + close(mPfd1); + // Intentionally don't close mPfd0 as it is used after this thread is exited to read the slop } -FtraceDriver::FtraceDriver() : mValues(NULL) { +FtraceDriver::FtraceDriver() : mValues(NULL), mSupported(false), mMonotonicRawSupport(false), mTracingOn(0) { } FtraceDriver::~FtraceDriver() { @@ -75,6 +287,31 @@ FtraceDriver::~FtraceDriver() { } void FtraceDriver::readEvents(mxml_node_t *const xml) { + // Check the kernel version + int release[3]; + if (!getLinuxVersion(release)) { + logg.logError("getLinuxVersion failed"); + handleException(); + } + + // The perf clock was added in 3.10 + const int kernelVersion = KERNEL_VERSION(release[0], release[1], release[2]); + if (kernelVersion < KERNEL_VERSION(3, 10, 0)) { + mSupported = false; + logg.logSetup("Ftrace Disabled\nFor full ftrace functionality please upgrade to Linux 3.10 or later. With user space gator and Linux prior to 3.10, ftrace counters with the tracepoint and arg attributes will be available."); + return; + } + mMonotonicRawSupport = kernelVersion >= KERNEL_VERSION(4, 2, 0); + + // Is debugfs or tracefs available? + if (access(TRACING_PATH, R_OK) != 0) { + mSupported = false; + logg.logSetup("Ftrace Disabled\nUnable to locate the tracing directory"); + return; + } + + mSupported = true; + mxml_node_t *node = xml; int count = 0; while (true) { @@ -93,26 +330,215 @@ void FtraceDriver::readEvents(mxml_node_t *const xml) { const char *regex = mxmlElementGetAttr(node, "regex"); if (regex == NULL) { - logg->logError(__FILE__, __LINE__, "The regex counter %s is missing the required regex attribute", counter); + logg.logError("The regex counter %s is missing the required regex attribute", counter); handleException(); } - setCounters(new FtraceCounter(getCounters(), strdup(counter), regex)); + + const char *tracepoint = mxmlElementGetAttr(node, "tracepoint"); + const char *enable = mxmlElementGetAttr(node, "enable"); + if (enable == NULL) { + enable = tracepoint; + } + if (gSessionData.mPerf.isSetup() && tracepoint != NULL) { + logg.logMessage("Not using ftrace for counter %s", counter); + continue; + } + if (enable != NULL) { + char buf[1<<10]; + snprintf(buf, sizeof(buf), EVENTS_PATH "/%s/enable", enable); + if (access(buf, W_OK) != 0) { + logg.logSetup("%s Disabled\n%s was not found", counter, buf); + continue; + } + } + + logg.logMessage("Using ftrace for %s", counter); + setCounters(new FtraceCounter(getCounters(), strdup(counter), enable)); ++count; } mValues = new int64_t[2*count]; } -int FtraceDriver::read(const char *line, int64_t **buf) { - int count = 0; +bool FtraceDriver::prepare(int *const ftraceFds) { + if (gSessionData.mFtraceRaw) { + // Don't want the performace impact of sending all formats so gator only sends it for the enabled counters. This means other counters need to be disabled + if (DriverSource::writeDriver(TRACING_PATH "/events/enable", "0") != 0) { + logg.logError("Unable to turn off all events"); + handleException(); + } + } + + for (FtraceCounter *counter = static_cast(getCounters()); counter != NULL; counter = static_cast(counter->getNext())) { + if (!counter->isEnabled()) { + continue; + } + counter->prepare(); + } + + if (DriverSource::readIntDriver(TRACING_PATH "/tracing_on", &mTracingOn)) { + logg.logError("Unable to read if ftrace is enabled"); + handleException(); + } + + if (DriverSource::writeDriver(TRACING_PATH "/tracing_on", "0") != 0) { + logg.logError("Unable to turn ftrace off before truncating the buffer"); + handleException(); + } + + { + int fd; + fd = open(TRACING_PATH "/trace", O_WRONLY | O_TRUNC | O_CLOEXEC, 0666); + if (fd < 0) { + logg.logError("Unable truncate ftrace buffer: %s", strerror(errno)); + handleException(); + } + close(fd); + } + + const char *const clock = mMonotonicRawSupport ? "mono_raw" : "perf"; + if (DriverSource::writeDriver(TRACING_PATH "/trace_clock", clock) != 0) { + logg.logError("Unable to switch ftrace to the %s clock, please ensure you are running Linux %s or later", clock, mMonotonicRawSupport ? "4.2" : "3.10"); + handleException(); + } + + if (!gSessionData.mFtraceRaw) { + ftraceFds[0] = open(TRACING_PATH "/trace_pipe", O_RDONLY | O_CLOEXEC); + if (ftraceFds[0] < 0) { + logg.logError("Unable to open trace_pipe"); + handleException(); + } + ftraceFds[1] = -1; + return true; + } + + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = handlerUsr1; + if (sigaction(SIGUSR1, &act, NULL) != 0) { + logg.logError("sigaction failed"); + handleException(); + } + + pageSize = sysconf(_SC_PAGESIZE); + if (pageSize <= 0) { + logg.logError("sysconf PAGESIZE failed"); + handleException(); + } + + mBarrier.init(gSessionData.mCores + 1); + + int cpu; + for (cpu = 0; cpu < gSessionData.mCores; ++cpu) { + int pfd[2]; + if (pipe2(pfd, O_CLOEXEC) != 0) { + logg.logError("pipe2 failed, %s (%i)", strerror(errno), errno); + handleException(); + } + + char buf[64]; + snprintf(buf, sizeof(buf), TRACING_PATH "/per_cpu/cpu%i/trace_pipe_raw", cpu); + const int tfd = open(buf, O_RDONLY | O_CLOEXEC); + (new FtraceReader(&mBarrier, cpu, tfd, pfd[0], pfd[1]))->start(); + ftraceFds[cpu] = pfd[0]; + } + ftraceFds[cpu] = -1; + + return false; +} + +void FtraceDriver::start() { + if (DriverSource::writeDriver(TRACING_PATH "/tracing_on", "1") != 0) { + logg.logError("Unable to turn ftrace on"); + handleException(); + } + + if (gSessionData.mFtraceRaw) { + mBarrier.wait(); + } +} + +void FtraceDriver::stop(int *const ftraceFds) { + DriverSource::writeDriver(TRACING_PATH "/tracing_on", mTracingOn); + + for (FtraceCounter *counter = static_cast(getCounters()); counter != NULL; counter = static_cast(counter->getNext())) { + if (!counter->isEnabled()) { + continue; + } + counter->stop(); + } + + if (!gSessionData.mFtraceRaw) { + ftraceFds[0] = -1; + } else { + int i = 0; + for (FtraceReader *reader = FtraceReader::getHead(); reader != NULL; reader = reader->getNext(), ++i) { + reader->interrupt(); + ftraceFds[i] = reader->getPfd0(); + } + ftraceFds[i] = -1; + for (FtraceReader *reader = FtraceReader::getHead(); reader != NULL; reader = reader->getNext(), ++i) { + reader->join(); + } + } + + // Reset back to local after joining with the reader threads as otherwise any remaining ftrace data is purged + DriverSource::writeDriver(TRACING_PATH "/trace_clock", "local"); +} + +bool FtraceDriver::readTracepointFormats(const uint64_t currTime, Buffer *const buffer, DynBuf *const printb, DynBuf *const b) { + if (!gSessionData.mFtraceRaw) { + return true; + } + + if (!printb->printf(EVENTS_PATH "/header_page")) { + logg.logMessage("DynBuf::printf failed"); + return false; + } + if (!b->read(printb->getBuf())) { + logg.logMessage("DynBuf::read failed"); + return false; + } + buffer->marshalHeaderPage(currTime, b->getBuf()); + + if (!printb->printf(EVENTS_PATH "/header_event")) { + logg.logMessage("DynBuf::printf failed"); + return false; + } + if (!b->read(printb->getBuf())) { + logg.logMessage("DynBuf::read failed"); + return false; + } + buffer->marshalHeaderEvent(currTime, b->getBuf()); + + DIR *dir = opendir(EVENTS_PATH "/ftrace"); + if (dir == NULL) { + logg.logError("Unable to open events ftrace folder"); + handleException(); + } + struct dirent *dirent; + while ((dirent = readdir(dir)) != NULL) { + if (dirent->d_name[0] == '.' || dirent->d_type != DT_DIR) { + continue; + } + if (!printb->printf(EVENTS_PATH "/ftrace/%s/format", dirent->d_name)) { + logg.logMessage("DynBuf::printf failed"); + return false; + } + if (!b->read(printb->getBuf())) { + logg.logMessage("DynBuf::read failed"); + return false; + } + buffer->marshalFormat(currTime, b->getLength(), b->getBuf()); + } + closedir(dir); for (FtraceCounter *counter = static_cast(getCounters()); counter != NULL; counter = static_cast(counter->getNext())) { if (!counter->isEnabled()) { continue; } - count += counter->read(line, mValues + 2*count); + counter->readTracepointFormat(currTime, buffer, printb, b); } - *buf = mValues; - return count; + return true; } diff --git a/tools/gator/daemon/FtraceDriver.h b/tools/gator/daemon/FtraceDriver.h index 5f958be..2da5808 100644 --- a/tools/gator/daemon/FtraceDriver.h +++ b/tools/gator/daemon/FtraceDriver.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2014. All rights reserved. + * Copyright (C) ARM Limited 2014-2015. 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 @@ -9,8 +9,27 @@ #ifndef FTRACEDRIVER_H #define FTRACEDRIVER_H +#include + #include "Driver.h" +class DynBuf; + +// The Android NDK doesn't provide an implementation of pthread_barrier_t, so implement our own +class Barrier { +public: + Barrier(); + ~Barrier(); + + void init(unsigned int count); + void wait(); + +private: + pthread_mutex_t mMutex; + pthread_cond_t mCond; + unsigned int mCount; +}; + class FtraceDriver : public SimpleDriver { public: FtraceDriver(); @@ -18,10 +37,20 @@ public: void readEvents(mxml_node_t *const xml); - int read(const char *line, int64_t **buf); + bool prepare(int *const ftraceFds); + void start(); + void stop(int *const ftraceFds); + bool readTracepointFormats(const uint64_t currTime, Buffer *const buffer, DynBuf *const printb, DynBuf *const b); + + bool isSupported() const { return mSupported; } private: int64_t *mValues; + Barrier mBarrier; + int mSupported : 1, + mMonotonicRawSupport : 1, + mUnused0 : 30; + int mTracingOn; // Intentionally unimplemented FtraceDriver(const FtraceDriver &); diff --git a/tools/gator/daemon/FtraceSource.cpp b/tools/gator/daemon/FtraceSource.cpp deleted file mode 100644 index 5216333..0000000 --- a/tools/gator/daemon/FtraceSource.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/** - * Copyright (C) ARM Limited 2010-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 "FtraceSource.h" - -#include -#include -#include -#include -#include - -#include "DriverSource.h" -#include "Logging.h" -#include "SessionData.h" - -static void handler(int signum) -{ - (void)signum; -}; - -FtraceSource::FtraceSource(sem_t *senderSem) : mFtraceFh(NULL), mBuffer(0, FRAME_BLOCK_COUNTER, 128*1024, senderSem), mTid(-1), mTracingOn(0) { -} - -FtraceSource::~FtraceSource() { -} - -bool FtraceSource::prepare() { - { - struct sigaction act; - act.sa_handler = handler; - act.sa_flags = (int)SA_RESETHAND; - if (sigaction(SIGUSR1, &act, NULL) != 0) { - logg->logError(__FILE__, __LINE__, "sigaction failed: %s\n", strerror(errno)); - handleException(); - } - } - - if (DriverSource::readIntDriver("/sys/kernel/debug/tracing/tracing_on", &mTracingOn)) { - logg->logError(__FILE__, __LINE__, "Unable to read if ftrace is enabled"); - handleException(); - } - - if (DriverSource::writeDriver("/sys/kernel/debug/tracing/tracing_on", "0") != 0) { - logg->logError(__FILE__, __LINE__, "Unable to turn ftrace off before truncating the buffer"); - handleException(); - } - - { - int fd; - fd = open("/sys/kernel/debug/tracing/trace", O_WRONLY | O_TRUNC | O_CLOEXEC, 0666); - if (fd < 0) { - logg->logError(__FILE__, __LINE__, "Unable truncate ftrace buffer: %s", strerror(errno)); - handleException(); - } - close(fd); - } - - if (DriverSource::writeDriver("/sys/kernel/debug/tracing/trace_clock", "perf") != 0) { - logg->logError(__FILE__, __LINE__, "Unable to switch ftrace to the perf clock, please ensure you are running Linux 3.10 or later"); - handleException(); - } - - mFtraceFh = fopen_cloexec("/sys/kernel/debug/tracing/trace_pipe", "rb"); - if (mFtraceFh == NULL) { - logg->logError(__FILE__, __LINE__, "Unable to open trace_pipe"); - handleException(); - } - - return true; -} - -void FtraceSource::run() { - prctl(PR_SET_NAME, (unsigned long)&"gatord-ftrace", 0, 0, 0); - mTid = syscall(__NR_gettid); - - if (DriverSource::writeDriver("/sys/kernel/debug/tracing/tracing_on", "1") != 0) { - logg->logError(__FILE__, __LINE__, "Unable to turn ftrace on"); - handleException(); - } - - while (gSessionData->mSessionIsActive) { - char buf[1<<12]; - - if (fgets(buf, sizeof(buf), mFtraceFh) == NULL) { - if (errno == EINTR) { - // Interrupted by interrupt - likely user request to terminate - break; - } - logg->logError(__FILE__, __LINE__, "Unable read trace data: %s", strerror(errno)); - handleException(); - } - - const uint64_t currTime = getTime(); - - char *const colon = strstr(buf, ": "); - if (colon == NULL) { - logg->logError(__FILE__, __LINE__, "Unable find colon: %s", buf); - handleException(); - } - *colon = '\0'; - - char *const space = strrchr(buf, ' '); - if (space == NULL) { - logg->logError(__FILE__, __LINE__, "Unable find space: %s", buf); - handleException(); - } - *colon = ':'; - - int64_t *data = NULL; - int count = gSessionData->ftraceDriver.read(colon + 2, &data); - if (count > 0) { - errno = 0; - const long long time = strtod(space, NULL) * 1000000000; - if (errno != 0) { - logg->logError(__FILE__, __LINE__, "Unable to parse time: %s", strerror(errno)); - handleException(); - } - mBuffer.event64(-1, time); - - for (int i = 0; i < count; ++i) { - mBuffer.event64(data[2*i + 0], data[2*i + 1]); - } - - mBuffer.check(currTime); - } - - } - - mBuffer.setDone(); - - DriverSource::writeDriver("/sys/kernel/debug/tracing/tracing_on", mTracingOn); - fclose(mFtraceFh); - DriverSource::writeDriver("/sys/kernel/debug/tracing/trace_clock", "local"); -} - -void FtraceSource::interrupt() { - // Closing the underlying file handle does not result in the read on the ftrace file handle to return, so send a signal to the thread - syscall(__NR_tgkill, getpid(), mTid, SIGUSR1); -} - -bool FtraceSource::isDone() { - return mBuffer.isDone(); -} - -void FtraceSource::write(Sender *sender) { - // Don't send ftrace data until the summary packet is sent so that monotonic delta is available - if (!gSessionData->mSentSummary) { - return; - } - if (!mBuffer.isDone()) { - mBuffer.write(sender); - } -} diff --git a/tools/gator/daemon/FtraceSource.h b/tools/gator/daemon/FtraceSource.h deleted file mode 100644 index 2391b88..0000000 --- a/tools/gator/daemon/FtraceSource.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (C) ARM Limited 2010-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. - */ - -#ifndef FTRACESOURCE_H -#define FTRACESOURCE_H - -#include -#include - -#include "Buffer.h" -#include "Source.h" - -class FtraceSource : public Source { -public: - FtraceSource(sem_t *senderSem); - ~FtraceSource(); - - bool prepare(); - void run(); - void interrupt(); - - bool isDone(); - void write(Sender *sender); - -private: - void waitFor(const int bytes); - - FILE *mFtraceFh; - Buffer mBuffer; - int mTid; - int mTracingOn; - - // Intentionally unimplemented - FtraceSource(const FtraceSource &); - FtraceSource &operator=(const FtraceSource &); -}; - -#endif // FTRACESOURCE_H diff --git a/tools/gator/daemon/HwmonDriver.cpp b/tools/gator/daemon/HwmonDriver.cpp index 9d161ae..de2a251 100644 --- a/tools/gator/daemon/HwmonDriver.cpp +++ b/tools/gator/daemon/HwmonDriver.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -23,7 +23,7 @@ static sensors_subfeature_type getInput(const sensors_feature_type type) { case SENSORS_FEATURE_CURR: return SENSORS_SUBFEATURE_CURR_INPUT; case SENSORS_FEATURE_HUMIDITY: return SENSORS_SUBFEATURE_HUMIDITY_INPUT; default: - logg->logError(__FILE__, __LINE__, "Unsupported hwmon feature %i", type); + logg.logError("Unsupported hwmon feature %i", type); handleException(); } }; @@ -33,112 +33,112 @@ public: HwmonCounter(DriverCounter *next, char *const name, const sensors_chip_name *chip, const sensors_feature *feature); ~HwmonCounter(); - const char *getLabel() const { return label; } - const char *getTitle() const { return title; } - bool isDuplicate() const { return duplicate; } - const char *getDisplay() const { return display; } - const char *getCounterClass() const { return counter_class; } - const char *getUnit() const { return unit; } - int getModifier() const { return modifier; } + const char *getLabel() const { return mLabel; } + const char *getTitle() const { return mTitle; } + bool isDuplicate() const { return mDuplicate; } + const char *getDisplay() const { return mDisplay; } + const char *getCounterClass() const { return mCounterClass; } + const char *getUnit() const { return mUnit; } + double getMultiplier() const { return mMultiplier; } int64_t read(); private: void init(const sensors_chip_name *chip, const sensors_feature *feature); - const sensors_chip_name *chip; - const sensors_feature *feature; - char *label; - const char *title; - const char *display; - const char *counter_class; - const char *unit; - double previous_value; - int modifier; - int monotonic: 1, - duplicate : 1; + const sensors_chip_name *mChip; + const sensors_feature *mFeature; + char *mLabel; + const char *mTitle; + const char *mDisplay; + const char *mCounterClass; + const char *mUnit; + double mPreviousValue; + double mMultiplier; + int mMonotonic: 1, + mDuplicate : 1; // Intentionally unimplemented HwmonCounter(const HwmonCounter &); HwmonCounter &operator=(const HwmonCounter &); }; -HwmonCounter::HwmonCounter(DriverCounter *next, char *const name, const sensors_chip_name *chip, const sensors_feature *feature) : DriverCounter(next, name), chip(chip), feature(feature), duplicate(false) { - label = sensors_get_label(chip, feature); +HwmonCounter::HwmonCounter(DriverCounter *next, char *const name, const sensors_chip_name *const chip, const sensors_feature *feature) : DriverCounter(next, name), mChip(chip), mFeature(feature), mDuplicate(false) { + mLabel = sensors_get_label(mChip, mFeature); - switch (feature->type) { + switch (mFeature->type) { case SENSORS_FEATURE_IN: - title = "Voltage"; - display = "maximum"; - counter_class = "absolute"; - unit = "V"; - modifier = 1000; - monotonic = false; + mTitle = "Voltage"; + mDisplay = "maximum"; + mCounterClass = "absolute"; + mUnit = "V"; + mMultiplier = 0.001; + mMonotonic = false; break; case SENSORS_FEATURE_FAN: - title = "Fan"; - display = "average"; - counter_class = "absolute"; - unit = "RPM"; - modifier = 1; - monotonic = false; + mTitle = "Fan"; + mDisplay = "average"; + mCounterClass = "absolute"; + mUnit = "RPM"; + mMultiplier = 1.0; + mMonotonic = false; break; case SENSORS_FEATURE_TEMP: - title = "Temperature"; - display = "maximum"; - counter_class = "absolute"; - unit = "°C"; - modifier = 1000; - monotonic = false; + mTitle = "Temperature"; + mDisplay = "maximum"; + mCounterClass = "absolute"; + mUnit = "°C"; + mMultiplier = 0.001; + mMonotonic = false; break; case SENSORS_FEATURE_POWER: - title = "Power"; - display = "maximum"; - counter_class = "absolute"; - unit = "W"; - modifier = 1000000; - monotonic = false; + mTitle = "Power"; + mDisplay = "maximum"; + mCounterClass = "absolute"; + mUnit = "W"; + mMultiplier = 0.000001; + mMonotonic = false; break; case SENSORS_FEATURE_ENERGY: - title = "Energy"; - display = "accumulate"; - counter_class = "delta"; - unit = "J"; - modifier = 1000000; - monotonic = true; + mTitle = "Energy"; + mDisplay = "accumulate"; + mCounterClass = "delta"; + mUnit = "J"; + mMultiplier = 0.000001; + mMonotonic = true; break; case SENSORS_FEATURE_CURR: - title = "Current"; - display = "maximum"; - counter_class = "absolute"; - unit = "A"; - modifier = 1000; - monotonic = false; + mTitle = "Current"; + mDisplay = "maximum"; + mCounterClass = "absolute"; + mUnit = "A"; + mMultiplier = 0.001; + mMonotonic = false; break; case SENSORS_FEATURE_HUMIDITY: - title = "Humidity"; - display = "average"; - counter_class = "absolute"; - unit = "%"; - modifier = 1000; - monotonic = false; + mTitle = "Humidity"; + mDisplay = "average"; + mCounterClass = "absolute"; + mUnit = "%"; + mMultiplier = 0.001; + mMonotonic = false; break; default: - logg->logError(__FILE__, __LINE__, "Unsupported hwmon feature %i", feature->type); + logg.logError("Unsupported hwmon feature %i", mFeature->type); handleException(); } for (HwmonCounter * counter = static_cast(next); counter != NULL; counter = static_cast(counter->getNext())) { - if (strcmp(label, counter->getLabel()) == 0 && strcmp(title, counter->getTitle()) == 0) { - duplicate = true; - counter->duplicate = true; + if (strcmp(mLabel, counter->getLabel()) == 0 && strcmp(mTitle, counter->getTitle()) == 0) { + mDuplicate = true; + counter->mDuplicate = true; break; } } } HwmonCounter::~HwmonCounter() { - free((void *)label); + free((void *)mLabel); } int64_t HwmonCounter::read() { @@ -147,19 +147,19 @@ int64_t HwmonCounter::read() { const sensors_subfeature *subfeature; // Keep in sync with the read check in HwmonDriver::readEvents - subfeature = sensors_get_subfeature(chip, feature, getInput(feature->type)); + subfeature = sensors_get_subfeature(mChip, mFeature, getInput(mFeature->type)); if (!subfeature) { - logg->logError(__FILE__, __LINE__, "No input value for hwmon sensor %s", label); + logg.logError("No input value for hwmon sensor %s", mLabel); handleException(); } - if (sensors_get_value(chip, subfeature->number, &value) != 0) { - logg->logError(__FILE__, __LINE__, "Can't get input value for hwmon sensor %s", label); + if (sensors_get_value(mChip, subfeature->number, &value) != 0) { + logg.logError("Can't get input value for hwmon sensor %s", mLabel); handleException(); } - result = (monotonic ? value - previous_value : value); - previous_value = value; + result = (mMonotonic ? value - mPreviousValue : value); + mPreviousValue = value; return result; } @@ -174,7 +174,7 @@ HwmonDriver::~HwmonDriver() { void HwmonDriver::readEvents(mxml_node_t *const) { int err = sensors_init(NULL); if (err) { - logg->logMessage("Failed to initialize libsensors! (%d)", err); + logg.logSetup("Libsensors Disabled\nInitialize failed (%d)", err); return; } sensors_sysfs_no_scaling = 1; @@ -209,7 +209,7 @@ void HwmonDriver::readEvents(mxml_node_t *const) { void HwmonDriver::writeEvents(mxml_node_t *root) const { root = mxmlNewElement(root, "category"); - mxmlElementSetAttr(root, "name", "hwmon"); + mxmlElementSetAttr(root, "name", "Hardware Monitor"); char buf[1024]; for (HwmonCounter *counter = static_cast(getCounters()); counter != NULL; counter = static_cast(counter->getNext())) { @@ -224,12 +224,14 @@ void HwmonDriver::writeEvents(mxml_node_t *root) const { mxmlElementSetAttr(node, "display", counter->getDisplay()); mxmlElementSetAttr(node, "class", counter->getCounterClass()); mxmlElementSetAttr(node, "units", counter->getUnit()); - if (counter->getModifier() != 1) { - mxmlElementSetAttrf(node, "modifier", "%d", counter->getModifier()); + if (counter->getMultiplier() != 1.0) { + mxmlElementSetAttrf(node, "multiplier", "%lf", counter->getMultiplier()); } if (strcmp(counter->getDisplay(), "average") == 0 || strcmp(counter->getDisplay(), "maximum") == 0) { mxmlElementSetAttr(node, "average_selection", "yes"); } + mxmlElementSetAttr(node, "series_composition", "overlay"); + mxmlElementSetAttr(node, "rendering_type", "line"); snprintf(buf, sizeof(buf), "libsensors %s sensor %s (%s)", counter->getTitle(), counter->getLabel(), counter->getName()); mxmlElementSetAttr(node, "description", buf); } diff --git a/tools/gator/daemon/HwmonDriver.h b/tools/gator/daemon/HwmonDriver.h index f28d825..f15d557 100644 --- a/tools/gator/daemon/HwmonDriver.h +++ b/tools/gator/daemon/HwmonDriver.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 diff --git a/tools/gator/daemon/KMod.cpp b/tools/gator/daemon/KMod.cpp index fe9dc6a..307d6fe 100644 --- a/tools/gator/daemon/KMod.cpp +++ b/tools/gator/daemon/KMod.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -83,13 +83,13 @@ void KMod::setupCounter(Counter &counter) { if (access(text, F_OK) == 0) { int count = counter.getCount(); if (DriverSource::writeReadDriver(text, &count) && counter.getCount() > 0) { - logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%i with a count of %d\n", counter.getType(), counter.getEvent(), counter.getCount()); + logg.logError("Cannot enable EBS for %s:%i with a count of %d", counter.getType(), counter.getEvent(), counter.getCount()); handleException(); } counter.setCount(count); } else if (counter.getCount() > 0) { ConfigurationXML::remove(); - logg->logError(__FILE__, __LINE__, "Event Based Sampling is only supported with kernel versions 3.0.0 and higher with CONFIG_PERF_EVENTS=y, and CONFIG_HW_PERF_EVENTS=y. The invalid configuration.xml has been removed.\n"); + logg.logError("Event Based Sampling is only supported with kernel versions 3.0.0 and higher with CONFIG_PERF_EVENTS=y, and CONFIG_HW_PERF_EVENTS=y. The invalid configuration.xml has been removed."); handleException(); } } diff --git a/tools/gator/daemon/KMod.h b/tools/gator/daemon/KMod.h index 900a60e..7f06b4b 100644 --- a/tools/gator/daemon/KMod.h +++ b/tools/gator/daemon/KMod.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 diff --git a/tools/gator/daemon/LocalCapture.cpp b/tools/gator/daemon/LocalCapture.cpp index d2a4b79..b529aa1 100644 --- a/tools/gator/daemon/LocalCapture.cpp +++ b/tools/gator/daemon/LocalCapture.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -25,9 +25,9 @@ LocalCapture::LocalCapture() {} LocalCapture::~LocalCapture() {} void LocalCapture::createAPCDirectory(char* target_path) { - gSessionData->mAPCDir = createUniqueDirectory(target_path, ".apc"); - if ((removeDirAndAllContents(gSessionData->mAPCDir) != 0 || mkdir(gSessionData->mAPCDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)) { - logg->logError(__FILE__, __LINE__, "Unable to create directory %s", gSessionData->mAPCDir); + gSessionData.mAPCDir = createUniqueDirectory(target_path, ".apc"); + if ((removeDirAndAllContents(gSessionData.mAPCDir) != 0 || mkdir(gSessionData.mAPCDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)) { + logg.logError("Unable to create directory %s", gSessionData.mAPCDir); handleException(); } } @@ -36,17 +36,17 @@ void LocalCapture::write(char* string) { char file[PATH_MAX]; // Set full path - snprintf(file, PATH_MAX, "%s/session.xml", gSessionData->mAPCDir); + snprintf(file, PATH_MAX, "%s/session.xml", gSessionData.mAPCDir); // Write the file - if (util->writeToDisk(file, string) < 0) { - logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file); + if (writeToDisk(file, string) < 0) { + logg.logError("Error writing %s\nPlease verify the path.", file); handleException(); } // Write events XML EventsXML eventsXML; - eventsXML.write(gSessionData->mAPCDir); + eventsXML.write(gSessionData.mAPCDir); } char* LocalCapture::createUniqueDirectory(const char* initialPath, const char* ending) { @@ -55,11 +55,11 @@ char* LocalCapture::createUniqueDirectory(const char* initialPath, const char* e // Ensure the path is an absolute path, i.e. starts with a slash if (initialPath == 0 || strlen(initialPath) == 0) { - logg->logError(__FILE__, __LINE__, "Missing -o command line option required for a local capture."); + logg.logError("Missing -o command line option required for a local capture."); handleException(); } else if (initialPath[0] != '/') { if (getcwd(path, PATH_MAX) == 0) { - logg->logMessage("Unable to retrieve the current working directory"); + logg.logMessage("Unable to retrieve the current working directory"); } strncat(path, "/", PATH_MAX - strlen(path) - 1); strncat(path, initialPath, PATH_MAX - strlen(path) - 1); @@ -114,16 +114,16 @@ void LocalCapture::copyImages(ImageLinkList* ptr) { char dstfilename[PATH_MAX]; while (ptr) { - strncpy(dstfilename, gSessionData->mAPCDir, PATH_MAX); + strncpy(dstfilename, gSessionData.mAPCDir, PATH_MAX); dstfilename[PATH_MAX - 1] = 0; // strncpy does not guarantee a null-terminated string - if (gSessionData->mAPCDir[strlen(gSessionData->mAPCDir) - 1] != '/') { + if (gSessionData.mAPCDir[strlen(gSessionData.mAPCDir) - 1] != '/') { strncat(dstfilename, "/", PATH_MAX - strlen(dstfilename) - 1); } - strncat(dstfilename, util->getFilePart(ptr->path), PATH_MAX - strlen(dstfilename) - 1); - if (util->copyFile(ptr->path, dstfilename)) { - logg->logMessage("copied file %s to %s", ptr->path, dstfilename); + strncat(dstfilename, getFilePart(ptr->path), PATH_MAX - strlen(dstfilename) - 1); + if (copyFile(ptr->path, dstfilename)) { + logg.logMessage("copied file %s to %s", ptr->path, dstfilename); } else { - logg->logMessage("copy of file %s to %s failed", ptr->path, dstfilename); + logg.logMessage("copy of file %s to %s failed", ptr->path, dstfilename); } ptr = ptr->next; diff --git a/tools/gator/daemon/LocalCapture.h b/tools/gator/daemon/LocalCapture.h index 25d281f..807f49d 100644 --- a/tools/gator/daemon/LocalCapture.h +++ b/tools/gator/daemon/LocalCapture.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 diff --git a/tools/gator/daemon/Logging.cpp b/tools/gator/daemon/Logging.cpp index 41ffa1a..ba5e315 100644 --- a/tools/gator/daemon/Logging.cpp +++ b/tools/gator/daemon/Logging.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -8,71 +8,75 @@ #include "Logging.h" +#include #include #include -#include #include -#ifdef WIN32 -#define MUTEX_INIT() mLoggingMutex = CreateMutex(NULL, false, NULL); -#define MUTEX_LOCK() WaitForSingleObject(mLoggingMutex, 0xFFFFFFFF); -#define MUTEX_UNLOCK() ReleaseMutex(mLoggingMutex); -#define snprintf _snprintf -#else -#include -#define MUTEX_INIT() pthread_mutex_init(&mLoggingMutex, NULL) -#define MUTEX_LOCK() pthread_mutex_lock(&mLoggingMutex) -#define MUTEX_UNLOCK() pthread_mutex_unlock(&mLoggingMutex) -#endif - // Global thread-safe logging -Logging* logg = NULL; +Logging logg; -Logging::Logging(bool debug) { - mDebug = debug; - MUTEX_INIT(); +Logging::Logging() : mDebug(true) { + pthread_mutex_init(&mLoggingMutex, NULL); strcpy(mErrBuf, "Unknown Error"); - strcpy(mLogBuf, "Unknown Message"); } Logging::~Logging() { } -void Logging::logError(const char* file, int line, const char* fmt, ...) { - va_list args; +static void format(char *const buf, const size_t bufSize, const bool verbose, const char *const level, const char *const function, const char *const file, const int line, const char *const fmt, va_list args) { + int len; - MUTEX_LOCK(); - if (mDebug) { - snprintf(mErrBuf, sizeof(mErrBuf), "ERROR[%s:%d]: ", file, line); + if (verbose) { + len = snprintf(buf, bufSize, "%s: %s(%s:%i): ", level, function, file, line); } else { - mErrBuf[0] = 0; + buf[0] = 0; + len = 0; } + vsnprintf(buf + len, bufSize - 1 - len, fmt, args); // subtract 1 for \0 +} + +void Logging::_logError(const char *function, const char *file, int line, const char *fmt, ...) { + va_list args; + + pthread_mutex_lock(&mLoggingMutex); va_start(args, fmt); - vsnprintf(mErrBuf + strlen(mErrBuf), sizeof(mErrBuf) - 2 - strlen(mErrBuf), fmt, args); // subtract 2 for \n and \0 + format(mErrBuf, sizeof(mErrBuf), mDebug, "ERROR", function, file, line, fmt, args); va_end(args); + pthread_mutex_unlock(&mLoggingMutex); - if (strlen(mErrBuf) > 0) { - strcat(mErrBuf, "\n"); + fprintf(stderr, "%s\n", mErrBuf); +} + +void Logging::_logSetup(const char *function, const char *file, int line, const char *fmt, ...) { + char logBuf[4096]; // Arbitrarily large buffer to hold a string + va_list args; + + va_start(args, fmt); + format(logBuf, sizeof(logBuf), mDebug, "SETUP", function, file, line, fmt, args); + va_end(args); + + pthread_mutex_lock(&mLoggingMutex); + mSetup.appendStr(logBuf); + mSetup.appendStr("|"); + pthread_mutex_unlock(&mLoggingMutex); + + if (mDebug) { + fprintf(stderr, "%s\n", logBuf); } - MUTEX_UNLOCK(); } -void Logging::logMessage(const char* fmt, ...) { +void Logging::_logMessage(const char *function, const char *file, int line, const char *fmt, ...) { if (mDebug) { + char logBuf[4096]; // Arbitrarily large buffer to hold a string va_list args; - MUTEX_LOCK(); - strcpy(mLogBuf, "INFO: "); - va_start(args, fmt); - vsnprintf(mLogBuf + strlen(mLogBuf), sizeof(mLogBuf) - 2 - strlen(mLogBuf), fmt, args); // subtract 2 for \n and \0 + format(logBuf, sizeof(logBuf), mDebug, "INFO", function, file, line, fmt, args); va_end(args); - strcat(mLogBuf, "\n"); - fprintf(stdout, "%s", mLogBuf); - fflush(stdout); - MUTEX_UNLOCK(); + fprintf(stderr, "%s\n", logBuf); } } diff --git a/tools/gator/daemon/Logging.h b/tools/gator/daemon/Logging.h index 09e93ff..37d3a6f 100644 --- a/tools/gator/daemon/Logging.h +++ b/tools/gator/daemon/Logging.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -11,25 +11,39 @@ #include +#include "DynBuf.h" + #define DRIVER_ERROR "\n Driver issue:\n >> gator.ko must be built against the current kernel version & configuration\n >> gator.ko should be co-located with gatord in the same directory\n >> OR insmod gator.ko prior to launching gatord" class Logging { public: - Logging(bool debug); + Logging(); ~Logging(); - void logError(const char* file, int line, const char* fmt, ...); - void logMessage(const char* fmt, ...); - char* getLastError() {return mErrBuf;} - char* getLastMessage() {return mLogBuf;} + + void setDebug(bool debug) { mDebug = debug; } + +#define logError(...) _logError(__func__, __FILE__, __LINE__, __VA_ARGS__) + __attribute__ ((format (printf, 5, 6))) + void _logError(const char *function, const char *file, int line, const char *fmt, ...); + const char *getLastError() {return mErrBuf;} + +#define logSetup(...) _logSetup(__func__, __FILE__, __LINE__, __VA_ARGS__) + __attribute__ ((format (printf, 5, 6))) + void _logSetup(const char *function, const char *file, int line, const char *fmt, ...); + const char *getSetup() {return mSetup.getBuf() == NULL ? "" : mSetup.getBuf();} + +#define logMessage(...) _logMessage(__func__, __FILE__, __LINE__, __VA_ARGS__) + __attribute__ ((format (printf, 5, 6))) + void _logMessage(const char *function, const char *file, int line, const char *fmt, ...); private: - char mErrBuf[4096]; // Arbitrarily large buffer to hold a string - char mLogBuf[4096]; // Arbitrarily large buffer to hold a string - bool mDebug; + DynBuf mSetup; pthread_mutex_t mLoggingMutex; + bool mDebug; + char mErrBuf[4096]; // Arbitrarily large buffer to hold a string }; -extern Logging* logg; +extern Logging logg; extern void handleException() __attribute__ ((noreturn)); diff --git a/tools/gator/daemon/Makefile b/tools/gator/daemon/Makefile index 27531b4..49b2ac3 100644 --- a/tools/gator/daemon/Makefile +++ b/tools/gator/daemon/Makefile @@ -6,7 +6,7 @@ # CROSS_COMPILE=/path/to/cross-compiler/arm-linux-gnueabihf- # NOTE: This toolchain uses the hardfloat abi by default. For non-hardfloat # targets run 'make SOFTFLOAT=1 SYSROOT=/path/to/sysroot', see -# README_Streamline.txt for more details +# streamline/gator/README.md for more details CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ diff --git a/tools/gator/daemon/MaliVideoDriver.cpp b/tools/gator/daemon/MaliVideoDriver.cpp index 5eef264..f72acfd 100644 --- a/tools/gator/daemon/MaliVideoDriver.cpp +++ b/tools/gator/daemon/MaliVideoDriver.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2014. All rights reserved. + * Copyright (C) ARM Limited 2014-2015. 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 @@ -58,6 +58,7 @@ MaliVideoDriver::~MaliVideoDriver() { } void MaliVideoDriver::readEvents(mxml_node_t *const xml) { + // Always create the counters as /dev/mv500 may show up after gatord starts mxml_node_t *node = xml; while (true) { node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND); @@ -82,12 +83,22 @@ void MaliVideoDriver::readEvents(mxml_node_t *const xml) { int MaliVideoDriver::writeCounters(mxml_node_t *root) const { if (access("/dev/mv500", F_OK) != 0) { + // Don't show the counters in counter configuration return 0; } return super::writeCounters(root); } +bool MaliVideoDriver::claimCounter(const Counter &counter) const { + if (access("/dev/mv500", F_OK) != 0) { + // Don't add the counters to captured XML + return 0; + } + + return super::claimCounter(counter); +} + void MaliVideoDriver::marshalEnable(const MaliVideoCounterType type, char *const buf, const size_t bufsize, int &pos) { // size int numEnabled = 0; @@ -104,20 +115,6 @@ void MaliVideoDriver::marshalEnable(const MaliVideoCounterType type, char *const } } -static bool writeAll(const int mveUds, const char *const buf, const int pos) { - int written = 0; - while (written < pos) { - size_t bytes = ::write(mveUds, buf + written, pos - written); - if (bytes <= 0) { - logg->logMessage("%s(%s:%i): write failed", __FUNCTION__, __FILE__, __LINE__); - return false; - } - written += bytes; - } - - return true; -} - bool MaliVideoDriver::start(const int mveUds) { char buf[256]; int pos = 0; @@ -146,9 +143,9 @@ bool MaliVideoDriver::start(const int mveUds) { // data_protocol_version Buffer::packInt(buf, sizeof(buf), pos, 1); // sample_rate - convert samples/second to ms/sample - Buffer::packInt(buf, sizeof(buf), pos, 1000/gSessionData->mSampleRate); + Buffer::packInt(buf, sizeof(buf), pos, gSessionData.mSampleRate/1000); // live_rate - convert ns/flush to ms/flush - Buffer::packInt(buf, sizeof(buf), pos, gSessionData->mLiveRate/1000000); + Buffer::packInt(buf, sizeof(buf), pos, gSessionData.mLiveRate/1000000); // code - MVE_INSTR_ENABLE_COUNTERS buf[pos++] = 'C'; @@ -183,7 +180,6 @@ void MaliVideoDriver::stop(const int mveUds) { buf[pos++] = 'T'; buf[pos++] = 'O'; buf[pos++] = 'P'; - marshalEnable(MVCT_COUNTER, buf, sizeof(buf), pos); writeAll(mveUds, buf, pos); diff --git a/tools/gator/daemon/MaliVideoDriver.h b/tools/gator/daemon/MaliVideoDriver.h index 204a57a..fd01b1b 100644 --- a/tools/gator/daemon/MaliVideoDriver.h +++ b/tools/gator/daemon/MaliVideoDriver.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2014. All rights reserved. + * Copyright (C) ARM Limited 2014-2015. 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 @@ -30,6 +30,7 @@ public: void readEvents(mxml_node_t *const root); int writeCounters(mxml_node_t *root) const; + bool claimCounter(const Counter &counter) const; bool start(const int mveUds); void stop(const int mveUds); diff --git a/tools/gator/daemon/MemInfoDriver.cpp b/tools/gator/daemon/MemInfoDriver.cpp index cce15c1..bc02107 100644 --- a/tools/gator/daemon/MemInfoDriver.cpp +++ b/tools/gator/daemon/MemInfoDriver.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -44,7 +44,7 @@ MemInfoDriver::~MemInfoDriver() { void MemInfoDriver::readEvents(mxml_node_t *const) { // Only for use with perf - if (!gSessionData->perf.isSetup()) { + if (!gSessionData.mPerf.isSetup()) { return; } @@ -59,7 +59,7 @@ void MemInfoDriver::read(Buffer *const buffer) { } if (!mBuf.read("/proc/meminfo")) { - logg->logError(__FILE__, __LINE__, "Failed to read /proc/meminfo"); + logg.logError("Failed to read /proc/meminfo"); handleException(); } diff --git a/tools/gator/daemon/MemInfoDriver.h b/tools/gator/daemon/MemInfoDriver.h index eb1b041..ffeaf30 100644 --- a/tools/gator/daemon/MemInfoDriver.h +++ b/tools/gator/daemon/MemInfoDriver.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 diff --git a/tools/gator/daemon/MidgardDriver.cpp b/tools/gator/daemon/MidgardDriver.cpp new file mode 100644 index 0000000..58ea868 --- /dev/null +++ b/tools/gator/daemon/MidgardDriver.cpp @@ -0,0 +1,325 @@ +/** + * Copyright (C) ARM Limited 2010-2015. 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 "MidgardDriver.h" + +#include + +#include "Buffer.h" +#include "Logging.h" +#include "OlySocket.h" +#include "SessionData.h" + +static const uint32_t PACKET_SHARED_PARAMETER = 0x0000; +static const uint32_t PACKET_HARDWARE_COUNTER_DIRECTORY = 0x0002; + +struct PacketHeader { + uint32_t mImplSpec : 8, + mReserved0 : 8, + mPacketIdentifier : 16; //mPacketId : 10, mPacketFamily : 6; + uint32_t mDataLength : 23, + mSequenceNumbered : 1, + mReserved1 : 8; +} __attribute__((packed)); + +struct SharedParameterPacket { + uint32_t mMaliMagic; + uint32_t mMaxDataLen : 24, + mReserved2 : 8; + uint32_t mPid; + uint32_t mOffsets[4]; +} __attribute__((packed)); + +struct HardwareCounter { + uint16_t mCounterIndex; + uint32_t mCounterNameLen; + char mCounterName[]; +} __attribute__((packed)); + +struct GPUPerfPeriod { + uint32_t mDeclId; + int32_t mMicroseconds; + uint32_t mStartIndex; + uint64_t mEnableMap; +} __attribute__((packed)); + +struct GLESWindump { + uint32_t mDeclId; + int32_t mSkipframes; + uint32_t mMinWidth; + uint32_t mMinHeight; +} __attribute__((packed)); + +struct CounterData { + enum { + PERF, + WINDUMP, + ACTIVITY, + } mType; + union { + struct { + // PERF + int mIndex; + }; + struct { + // WINDUMP + }; + struct { + // ACTIVITY + int mCores; + }; + }; +}; + +class MidgardCounter : public DriverCounter { +public: + MidgardCounter(DriverCounter *next, const char *name, CounterData *const counterData) : DriverCounter(next, name), mCounterData(*counterData), mEvent(-1) {} + + ~MidgardCounter() { + } + + int getType() const { return mCounterData.mType; } + + // PERF + int getIndex() const { return mCounterData.mIndex; } + + // ACTIVITY + int getCores() const { return mCounterData.mCores; } + + void setEvent(const int event) { mEvent = event; } + int getEvent() const { return mEvent; } + +private: + const CounterData mCounterData; + int mEvent; + + // Intentionally undefined + MidgardCounter(const MidgardCounter &); + MidgardCounter &operator=(const MidgardCounter &); +}; + +MidgardDriver::MidgardDriver() : mQueried(false) { +} + +MidgardDriver::~MidgardDriver() { +} + +void MidgardDriver::query() const { + if (mQueried) { + return; + } + // Only try once even if it fails otherwise not all the possible counters may be shown + mQueried = true; + + char *const buf = gSessionData.mSharedData->mMaliMidgardCounters; + // Prefer not to requery once obtained as it could throw capture off, assume it doesn't change + if (gSessionData.mSharedData->mMaliMidgardCountersSize > 0) { + logg.logMessage("Using cached Midgard counters\n"); + } else { + int uds = OlySocket::connect(MALI_GRAPHICS, MALI_GRAPHICS_SIZE); + if (uds < 0) { + logg.logMessage("Unable to connect to Midgard"); + } else { + logg.logMessage("Connected to midgard"); + gSessionData.mSharedData->mMaliMidgardCountersSize = 0; + + PacketHeader header; + const size_t bufSize = sizeof(gSessionData.mSharedData->mMaliMidgardCounters); + bool first = true; + // [DR] Do something with this + //uint32_t compatibilityTiebreak = 0; + + while (true) { + // [DR] Store-and-forward data at capture start? + if (!readAll(uds, &header, sizeof(PacketHeader))) { + logg.logError("Unable to read Midgard header"); + handleException(); + } + if (first && ((uint8_t *)&header)[0] != 0) { + logg.logMessage("Midgard data is not in encapsulated format"); + break; + } + first = false; + if (header.mDataLength > bufSize || !readAll(uds, buf, header.mDataLength)) { + logg.logError("Unable to read Midgard body"); + handleException(); + } + + if (header.mSequenceNumbered) { + logg.logError("sequence_numbered is true and is unsupported"); + handleException(); + } + + if (header.mReserved0 != 0 || header.mReserved1 != 0) { + continue; + } + + switch (header.mPacketIdentifier) { + case PACKET_SHARED_PARAMETER: { + const SharedParameterPacket *const packet = (SharedParameterPacket *)buf; + if (header.mDataLength >= sizeof(SharedParameterPacket) && header.mImplSpec == 0 && packet->mReserved2 == 0) { + if (packet->mMaliMagic != 0x6D616C69) { + logg.logError("mali_magic does not match expected value"); + handleException(); + } + /* + for (int i = 0; reinterpret_cast(packet->mOffsets + i + 1) <= buf + header.mDataLength && packet->mOffsets[i] != 0; ++i) { + if (i == 3) { + compatibilityTiebreak = *reinterpret_cast(buf + packet->mOffsets[i]); + printf("compatibility tiebreak: %i\n", compatibilityTiebreak); + } + } + */ + } + break; + } + + case PACKET_HARDWARE_COUNTER_DIRECTORY: { + if (header.mImplSpec == 0) { + gSessionData.mSharedData->mMaliMidgardCountersSize = header.mDataLength; + goto allDone; + } + } + + case 0x0400: + case 0x0402: + case 0x0408: + // Ignore + break; + + default: + // Unrecognized packet, give up + goto allDone; + } + } + allDone: + + close(uds); + } + } + + const size_t size = gSessionData.mSharedData->mMaliMidgardCountersSize; + CounterData cd; + cd.mType = CounterData::PERF; + for (int i = 0; i + sizeof(MidgardCounter) < size;) { + const HardwareCounter *counter = (HardwareCounter *)(buf + i); + char *name; + if (asprintf(&name, "ARM_Mali-%s", counter->mCounterName) <= 0) { + logg.logError("asprintf failed"); + handleException(); + } + cd.mIndex = counter->mCounterIndex; + ((MidgardDriver *)(this))->setCounters(new MidgardCounter(getCounters(), name, &cd)); + i += sizeof(*counter) + counter->mCounterNameLen; + } + + // Should a more sophisticated check be used? + if (size > 0) { + cd.mType = CounterData::WINDUMP; + ((MidgardDriver *)(this))->setCounters(new MidgardCounter(getCounters(), strdup("ARM_Mali-Midgard_Filmstrip2_cnt0"), &cd)); + + cd.mType = CounterData::ACTIVITY; + cd.mCores = 1; + ((MidgardDriver *)(this))->setCounters(new MidgardCounter(getCounters(), strdup("ARM_Mali-Midgard_fragment"), &cd)); + ((MidgardDriver *)(this))->setCounters(new MidgardCounter(getCounters(), strdup("ARM_Mali-Midgard_vertex"), &cd)); + ((MidgardDriver *)(this))->setCounters(new MidgardCounter(getCounters(), strdup("ARM_Mali-Midgard_opencl"), &cd)); + } +} + +bool MidgardDriver::start(const int uds) { + uint64_t enabled[8] = { 0 }; + size_t bufPos = 0; + char buf[ARRAY_LENGTH(enabled)*sizeof(GPUPerfPeriod) + sizeof(GLESWindump)]; + + for (MidgardCounter *counter = static_cast(getCounters()); counter != NULL; counter = static_cast(counter->getNext())) { + if (!counter->isEnabled() || counter->getType() != CounterData::PERF) { + continue; + } + + int i = counter->getIndex()/64; + if (i >= ARRAY_LENGTH(enabled)) { + logg.logError("enabled is too small"); + handleException(); + } + enabled[i] |= 1 << (counter->getIndex() & 63); + } + + for (int i = 0; i < ARRAY_LENGTH(enabled); ++i) { + if (enabled[i] == 0) { + continue; + } + + GPUPerfPeriod m; + // MALI_GPUPERF_PERIOD + m.mDeclId = 0; + m.mMicroseconds = gSessionData.mSampleRate > 0 ? 1000000/gSessionData.mSampleRate : 100000; + m.mStartIndex = 64*i; + m.mEnableMap = enabled[i]; + memcpy(buf + bufPos, &m, sizeof(m)); + bufPos += sizeof(m); + } + + bool foundWindumpCounter = false; + for (MidgardCounter *counter = static_cast(getCounters()); counter != NULL; counter = static_cast(counter->getNext())) { + if (!counter->isEnabled() || counter->getType() != CounterData::WINDUMP) { + continue; + } + + if (foundWindumpCounter) { + logg.logError("Only one Mali Midgard filmstrip counter can be enabled at a time"); + handleException(); + } + foundWindumpCounter = true; + + // MALI_GLES_WINDUMP + GLESWindump m; + m.mDeclId = 1; + m.mSkipframes = counter->getEvent() & 0xff; + m.mMinWidth = (counter->getEvent() & 0xfff00000) >> 20; + m.mMinHeight = (counter->getEvent() & 0xfff00) >> 8; + memcpy(buf + bufPos, &m, sizeof(m)); + bufPos += sizeof(m); + } + + if (bufPos > sizeof(buf)) { + logg.logError("Buffer overflow"); + handleException(); + } + if (!writeAll(uds, buf, bufPos)) { + logg.logError("Unable enable Midgard counters"); + handleException(); + } + + return true; +} + +bool MidgardDriver::claimCounter(const Counter &counter) const { + query(); + return super::claimCounter(counter); +} + +void MidgardDriver::resetCounters() { + query(); + super::resetCounters(); +} + +void MidgardDriver::setupCounter(Counter &counter) { + MidgardCounter *const midgardCounter = static_cast(findCounter(counter)); + if (midgardCounter == NULL) { + counter.setEnabled(false); + return; + } + midgardCounter->setEnabled(true); + counter.setKey(midgardCounter->getKey()); + if (counter.getEvent() != -1) { + midgardCounter->setEvent(counter.getEvent()); + } + if (midgardCounter->getType() == CounterData::ACTIVITY && midgardCounter->getCores() > 0) { + counter.setCores(midgardCounter->getCores()); + } +} diff --git a/tools/gator/daemon/MidgardDriver.h b/tools/gator/daemon/MidgardDriver.h new file mode 100644 index 0000000..5038985 --- /dev/null +++ b/tools/gator/daemon/MidgardDriver.h @@ -0,0 +1,37 @@ +/** + * Copyright (C) ARM Limited 2010-2015. 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. + */ + +#ifndef MIDGARDDRIVER_H +#define MIDGARDDRIVER_H + +#include "Driver.h" + +class MidgardDriver : public SimpleDriver { + typedef SimpleDriver super; + +public: + MidgardDriver(); + ~MidgardDriver(); + + bool claimCounter(const Counter &counter) const; + void resetCounters(); + void setupCounter(Counter &counter); + + bool start(const int midgardUds); + +private: + void query() const; + + mutable bool mQueried; + + // Intentionally unimplemented + MidgardDriver(const MidgardDriver &); + MidgardDriver &operator=(const MidgardDriver &); +}; + +#endif // MIDGARDDRIVER_H diff --git a/tools/gator/daemon/Monitor.cpp b/tools/gator/daemon/Monitor.cpp index 74f22ee..c71870b 100644 --- a/tools/gator/daemon/Monitor.cpp +++ b/tools/gator/daemon/Monitor.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -38,17 +38,17 @@ bool Monitor::init() { mFd = epoll_create(16); #endif if (mFd < 0) { - logg->logMessage("%s(%s:%i): epoll_create1 failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("epoll_create1 failed"); return false; } #ifndef EPOLL_CLOEXEC - int fdf = fcntl(mFd, F_GETFD); - if ((fdf == -1) || (fcntl(mFd, F_SETFD, fdf | FD_CLOEXEC) != 0)) { - logg->logMessage("%s(%s:%i): fcntl failed", __FUNCTION__, __FILE__, __LINE__); - ::close(mFd); - return -1; - } + int fdf = fcntl(mFd, F_GETFD); + if ((fdf == -1) || (fcntl(mFd, F_SETFD, fdf | FD_CLOEXEC) != 0)) { + logg.logMessage("fcntl failed"); + ::close(mFd); + return -1; + } #endif return true; @@ -60,7 +60,7 @@ bool Monitor::add(const int fd) { event.data.fd = fd; event.events = EPOLLIN; if (epoll_ctl(mFd, EPOLL_CTL_ADD, fd, &event) != 0) { - logg->logMessage("%s(%s:%i): epoll_ctl failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("epoll_ctl failed"); return false; } @@ -74,7 +74,7 @@ int Monitor::wait(struct epoll_event *const events, int maxevents, int timeout) if (errno == EINTR) { result = 0; } else { - logg->logMessage("%s(%s:%i): epoll_wait failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("epoll_wait failed"); } } diff --git a/tools/gator/daemon/Monitor.h b/tools/gator/daemon/Monitor.h index 7194e0e..55368fc 100644 --- a/tools/gator/daemon/Monitor.h +++ b/tools/gator/daemon/Monitor.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 diff --git a/tools/gator/daemon/NetDriver.cpp b/tools/gator/daemon/NetDriver.cpp index e75c069..50a187d 100644 --- a/tools/gator/daemon/NetDriver.cpp +++ b/tools/gator/daemon/NetDriver.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -52,7 +52,7 @@ NetDriver::~NetDriver() { void NetDriver::readEvents(mxml_node_t *const) { // Only for use with perf - if (!gSessionData->perf.isSetup()) { + if (!gSessionData.mPerf.isSetup()) { return; } @@ -108,7 +108,7 @@ bool NetDriver::doRead() { void NetDriver::start() { if (!doRead()) { - logg->logError(__FILE__, __LINE__, "Unable to read network stats"); + logg.logError("Unable to read network stats"); handleException(); } // Initialize previous values @@ -122,7 +122,7 @@ void NetDriver::start() { void NetDriver::read(Buffer *const buffer) { if (!doRead()) { - logg->logError(__FILE__, __LINE__, "Unable to read network stats"); + logg.logError("Unable to read network stats"); handleException(); } super::read(buffer); diff --git a/tools/gator/daemon/NetDriver.h b/tools/gator/daemon/NetDriver.h index 50ff850..5f72280 100644 --- a/tools/gator/daemon/NetDriver.h +++ b/tools/gator/daemon/NetDriver.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 diff --git a/tools/gator/daemon/OlySocket.cpp b/tools/gator/daemon/OlySocket.cpp index aa0ce49..2cd5a59 100644 --- a/tools/gator/daemon/OlySocket.cpp +++ b/tools/gator/daemon/OlySocket.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -11,7 +11,7 @@ #include #include #ifdef WIN32 -#include +#include #include #else #include @@ -19,6 +19,7 @@ #include #include #include +#include #endif #include "Logging.h" @@ -78,7 +79,7 @@ OlyServerSocket::OlyServerSocket(int port) { #ifdef WIN32 WSADATA wsaData; if (WSAStartup(0x0202, &wsaData) != 0) { - logg->logError(__FILE__, __LINE__, "Windows socket initialization failed"); + logg.logError("Windows socket initialization failed"); handleException(); } #endif @@ -97,11 +98,11 @@ OlySocket::OlySocket(int socketID) : mSocketID(socketID) { __a > __b ? __b : __a; \ }) -OlyServerSocket::OlyServerSocket(const char* path, const size_t pathSize) { +OlyServerSocket::OlyServerSocket(const char* path, const size_t pathSize, const bool calculateAddrlen) { // Create socket mFDServer = socket_cloexec(PF_UNIX, SOCK_STREAM, 0); if (mFDServer < 0) { - logg->logError(__FILE__, __LINE__, "Error creating server socket"); + logg.logError("Error creating server socket"); handleException(); } @@ -113,19 +114,19 @@ OlyServerSocket::OlyServerSocket(const char* path, const size_t pathSize) { sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0'; // Bind the socket to an address - if (bind(mFDServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { - logg->logError(__FILE__, __LINE__, "Binding of server socket failed."); + if (bind(mFDServer, (const struct sockaddr*)&sockaddr, calculateAddrlen ? offsetof(struct sockaddr_un, sun_path) + pathSize - 1 : sizeof(sockaddr)) < 0) { + logg.logError("Binding of server socket failed."); handleException(); } // Listen for connections on this socket if (listen(mFDServer, 1) < 0) { - logg->logError(__FILE__, __LINE__, "Listening of server socket failed"); + logg.logError("Listening of server socket failed"); handleException(); } } -int OlySocket::connect(const char* path, const size_t pathSize) { +int OlySocket::connect(const char* path, const size_t pathSize, const bool calculateAddrlen) { int fd = socket_cloexec(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) { return -1; @@ -138,7 +139,7 @@ int OlySocket::connect(const char* path, const size_t pathSize) { memcpy(sockaddr.sun_path, path, MIN(pathSize, sizeof(sockaddr.sun_path))); sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0'; - if (::connect(fd, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { + if (::connect(fd, (const struct sockaddr*)&sockaddr, calculateAddrlen ? offsetof(struct sockaddr_un, sun_path) + pathSize - 1 : sizeof(sockaddr)) < 0) { close(fd); return -1; } @@ -174,11 +175,11 @@ void OlySocket::closeSocket() { } void OlyServerSocket::closeServerSocket() { - if (CLOSE_SOCKET(mFDServer) != 0) { - logg->logError(__FILE__, __LINE__, "Failed to close server socket."); + if (mFDServer > 0 && CLOSE_SOCKET(mFDServer) != 0) { + logg.logError("Failed to close server socket."); handleException(); } - mFDServer = 0; + mFDServer = -1; } void OlyServerSocket::createServerSocket(int port) { @@ -190,7 +191,7 @@ void OlyServerSocket::createServerSocket(int port) { family = AF_INET; mFDServer = socket_cloexec(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (mFDServer < 0) { - logg->logError(__FILE__, __LINE__, "Error creating server socket"); + logg.logError("Error creating server socket"); handleException(); } } @@ -198,10 +199,16 @@ void OlyServerSocket::createServerSocket(int port) { // Enable address reuse, another solution would be to create the server socket once and only close it when the object exits int on = 1; if (setsockopt(mFDServer, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) != 0) { - logg->logError(__FILE__, __LINE__, "Setting server socket options failed"); + logg.logError("Setting server socket reuse option failed"); handleException(); } + // Listen on both IPv4 and IPv6 + on = 0; + if (setsockopt(mFDServer, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(on)) != 0) { + logg.logMessage("setsockopt IPV6_V6ONLY failed"); + } + // Create sockaddr_in structure, ensuring non-populated fields are zero struct sockaddr_in6 sockaddr; memset((void*)&sockaddr, 0, sizeof(sockaddr)); @@ -211,13 +218,13 @@ void OlyServerSocket::createServerSocket(int port) { // Bind the socket to an address if (bind(mFDServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { - logg->logError(__FILE__, __LINE__, "Binding of server socket failed.\nIs an instance already running?"); + logg.logError("Binding of server socket on port %i failed.\nIs an instance already running or is another application using that port?", port); handleException(); } // Listen for connections on this socket if (listen(mFDServer, 1) < 0) { - logg->logError(__FILE__, __LINE__, "Listening of server socket failed"); + logg.logError("Listening of server socket failed"); handleException(); } } @@ -227,14 +234,14 @@ void OlyServerSocket::createServerSocket(int port) { int OlyServerSocket::acceptConnection() { int socketID; if (mFDServer <= 0) { - logg->logError(__FILE__, __LINE__, "Attempting multiple connections on a single connection server socket or attempting to accept on a client socket"); + logg.logError("Attempting multiple connections on a single connection server socket or attempting to accept on a client socket"); handleException(); } // Accept a connection, note that this call blocks until a client connects socketID = accept_cloexec(mFDServer, NULL, NULL); if (socketID < 0) { - logg->logError(__FILE__, __LINE__, "Socket acceptance failed"); + logg.logError("Socket acceptance failed"); handleException(); } return socketID; @@ -248,7 +255,7 @@ void OlySocket::send(const char* buffer, int size) { while (size > 0) { int n = ::send(mSocketID, buffer, size, 0); if (n < 0) { - logg->logError(__FILE__, __LINE__, "Socket send error"); + logg.logError("Socket send error"); handleException(); } size -= n; @@ -264,10 +271,10 @@ int OlySocket::receive(char* buffer, int size) { int bytes = recv(mSocketID, buffer, size, 0); if (bytes < 0) { - logg->logError(__FILE__, __LINE__, "Socket receive error"); + logg.logError("Socket receive error"); handleException(); } else if (bytes == 0) { - logg->logMessage("Socket disconnected"); + logg.logMessage("Socket disconnected"); return -1; } return bytes; @@ -279,10 +286,10 @@ int OlySocket::receiveNBytes(char* buffer, int size) { while (size > 0 && buffer != NULL) { bytes = recv(mSocketID, buffer, size, 0); if (bytes < 0) { - logg->logError(__FILE__, __LINE__, "Socket receive error"); + logg.logError("Socket receive error"); handleException(); } else if (bytes == 0) { - logg->logMessage("Socket disconnected"); + logg.logMessage("Socket disconnected"); return -1; } buffer += bytes; @@ -304,10 +311,10 @@ int OlySocket::receiveString(char* buffer, int size) { // Receive a single character int bytes = recv(mSocketID, &buffer[bytes_received], 1, 0); if (bytes < 0) { - logg->logError(__FILE__, __LINE__, "Socket receive error"); + logg.logError("Socket receive error"); handleException(); } else if (bytes == 0) { - logg->logMessage("Socket disconnected"); + logg.logMessage("Socket disconnected"); return -1; } diff --git a/tools/gator/daemon/OlySocket.h b/tools/gator/daemon/OlySocket.h index 6b53b01..dcd557d 100644 --- a/tools/gator/daemon/OlySocket.h +++ b/tools/gator/daemon/OlySocket.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -12,15 +12,17 @@ #include #ifdef WIN32 -typedef socklen_t int; +typedef int socklen_t; #else #include #endif +#include "Config.h" + class OlySocket { public: #ifndef WIN32 - static int connect(const char* path, const size_t pathSize); + static int connect(const char* path, const size_t pathSize, const bool calculateAddrlen = false); #endif OlySocket(int socketID); @@ -43,7 +45,7 @@ class OlyServerSocket { public: OlyServerSocket(int port); #ifndef WIN32 - OlyServerSocket(const char* path, const size_t pathSize); + OlyServerSocket(const char* path, const size_t pathSize, const bool calculateAddrlen = false); #endif ~OlyServerSocket(); diff --git a/tools/gator/daemon/OlyUtility.cpp b/tools/gator/daemon/OlyUtility.cpp index 45340a2..30655f0 100644 --- a/tools/gator/daemon/OlyUtility.cpp +++ b/tools/gator/daemon/OlyUtility.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -21,9 +21,7 @@ #include #endif -OlyUtility* util = NULL; - -bool OlyUtility::stringToBool(const char* string, bool defValue) { +bool stringToBool(const char* string, bool defValue) { char value[32]; if (string == NULL) { @@ -52,7 +50,7 @@ bool OlyUtility::stringToBool(const char* string, bool defValue) { } } -void OlyUtility::stringToLower(char* string) { +void stringToLower(char* string) { if (string == NULL) { return; } @@ -64,7 +62,7 @@ void OlyUtility::stringToLower(char* string) { } // Modifies fullpath with the path part including the trailing path separator -int OlyUtility::getApplicationFullPath(char* fullpath, int sizeOfPath) { +int getApplicationFullPath(char* fullpath, int sizeOfPath) { memset(fullpath, 0, sizeOfPath); #if defined(WIN32) int length = GetModuleFileName(NULL, fullpath, sizeOfPath); @@ -88,7 +86,7 @@ int OlyUtility::getApplicationFullPath(char* fullpath, int sizeOfPath) { return 0; } -char* OlyUtility::readFromDisk(const char* file, unsigned int *size, bool appendNull) { +char* readFromDisk(const char* file, unsigned int *size, bool appendNull) { // Open the file FILE* pFile = fopen(file, "rb"); if (pFile==NULL) { @@ -128,7 +126,7 @@ char* OlyUtility::readFromDisk(const char* file, unsigned int *size, bool append return buffer; } -int OlyUtility::writeToDisk(const char* path, const char* data) { +int writeToDisk(const char* path, const char* data) { // Open the file FILE* pFile = fopen(path, "wb"); if (pFile == NULL) { @@ -146,7 +144,7 @@ int OlyUtility::writeToDisk(const char* path, const char* data) { return 0; } -int OlyUtility::appendToDisk(const char* path, const char* data) { +int appendToDisk(const char* path, const char* data) { // Open the file FILE* pFile = fopen(path, "a"); if (pFile == NULL) { @@ -170,7 +168,7 @@ int OlyUtility::appendToDisk(const char* path, const char* data) { * 0 is returned on an error; otherwise 1. */ #define TRANSFER_SIZE 1024 -int OlyUtility::copyFile(const char* srcFile, const char* dstFile) { +int copyFile(const char* srcFile, const char* dstFile) { char buffer[TRANSFER_SIZE]; FILE * f_src = fopen(srcFile,"rb"); if (!f_src) { @@ -200,7 +198,7 @@ int OlyUtility::copyFile(const char* srcFile, const char* dstFile) { return 1; } -const char* OlyUtility::getFilePart(const char* path) { +const char* getFilePart(const char* path) { const char* last_sep = strrchr(path, PATH_SEPARATOR); // in case path is not a full path @@ -213,7 +211,7 @@ const char* OlyUtility::getFilePart(const char* path) { // getPathPart may modify the contents of path // returns the path including the trailing path separator -char* OlyUtility::getPathPart(char* path) { +char* getPathPart(char* path) { char* last_sep = strrchr(path, PATH_SEPARATOR); // in case path is not a full path diff --git a/tools/gator/daemon/OlyUtility.h b/tools/gator/daemon/OlyUtility.h index 1d26beb..fc10e35 100644 --- a/tools/gator/daemon/OlyUtility.h +++ b/tools/gator/daemon/OlyUtility.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -21,22 +21,14 @@ #define CAIMAN_PATH_MAX PATH_MAX #endif -class OlyUtility { -public: - OlyUtility() {}; - ~OlyUtility() {}; - bool stringToBool(const char* string, bool defValue); - void stringToLower(char* string); - int getApplicationFullPath(char* path, int sizeOfPath); - char* readFromDisk(const char* file, unsigned int *size = NULL, bool appendNull = true); - int writeToDisk(const char* path, const char* file); - int appendToDisk(const char* path, const char* file); - int copyFile(const char* srcFile, const char* dstFile); - const char* getFilePart(const char* path); - char* getPathPart(char* path); -private: -}; - -extern OlyUtility* util; +bool stringToBool(const char* string, bool defValue); +void stringToLower(char* string); +int getApplicationFullPath(char* path, int sizeOfPath); +char* readFromDisk(const char* file, unsigned int *size = NULL, bool appendNull = true); +int writeToDisk(const char* path, const char* file); +int appendToDisk(const char* path, const char* file); +int copyFile(const char* srcFile, const char* dstFile); +const char* getFilePart(const char* path); +char* getPathPart(char* path); #endif // OLY_UTILITY_H diff --git a/tools/gator/daemon/PerfBuffer.cpp b/tools/gator/daemon/PerfBuffer.cpp index f127c99..a369688 100644 --- a/tools/gator/daemon/PerfBuffer.cpp +++ b/tools/gator/daemon/PerfBuffer.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -27,7 +27,7 @@ PerfBuffer::PerfBuffer() { PerfBuffer::~PerfBuffer() { for (int cpu = ARRAY_LENGTH(mBuf) - 1; cpu >= 0; --cpu) { if (mBuf[cpu] != MAP_FAILED) { - munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE); + munmap(mBuf[cpu], gSessionData.mPageSize + BUF_SIZE); } } } @@ -35,14 +35,14 @@ PerfBuffer::~PerfBuffer() { bool PerfBuffer::useFd(const int cpu, const int fd) { if (mFds[cpu] < 0) { if (mBuf[cpu] != MAP_FAILED) { - logg->logMessage("%s(%s:%i): cpu %i already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__, cpu); + logg.logMessage("cpu %i already online or not correctly cleaned up", cpu); return false; } // The buffer isn't mapped yet - mBuf[cpu] = mmap(NULL, gSessionData->mPageSize + BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + mBuf[cpu] = mmap(NULL, gSessionData.mPageSize + BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (mBuf[cpu] == MAP_FAILED) { - logg->logMessage("%s(%s:%i): mmap failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("mmap failed"); return false; } mFds[cpu] = fd; @@ -50,17 +50,17 @@ bool PerfBuffer::useFd(const int cpu, const int fd) { // Check the version struct perf_event_mmap_page *pemp = static_cast(mBuf[cpu]); if (pemp->compat_version != 0) { - logg->logMessage("%s(%s:%i): Incompatible perf_event_mmap_page compat_version", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("Incompatible perf_event_mmap_page compat_version"); return false; } } else { if (mBuf[cpu] == MAP_FAILED) { - logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("cpu already online or not correctly cleaned up"); return false; } if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, mFds[cpu]) < 0) { - logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("ioctl failed"); return false; } } @@ -75,12 +75,12 @@ void PerfBuffer::discard(const int cpu) { } bool PerfBuffer::isEmpty() { - for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + for (int cpu = 0; cpu < gSessionData.mCores; ++cpu) { if (mBuf[cpu] != MAP_FAILED) { // Take a snapshot of the positions struct perf_event_mmap_page *pemp = static_cast(mBuf[cpu]); - const __u64 head = pemp->data_head; - const __u64 tail = pemp->data_tail; + const __u64 head = ACCESS_ONCE(pemp->data_head); + const __u64 tail = ACCESS_ONCE(pemp->data_tail); if (head != tail) { return false; @@ -91,68 +91,133 @@ bool PerfBuffer::isEmpty() { return true; } -static void compressAndSend(const int cpu, const __u64 head, __u64 tail, const uint8_t *const b, Sender *const sender) { - // Pick a big size but something smaller than the chunkSize in Sender::writeData which is 100k - char buf[1<<16]; - int writePos = 0; - const int typeLength = gSessionData->mLocalCapture ? 0 : 1; +bool PerfBuffer::isFull() { + for (int cpu = 0; cpu < gSessionData.mCores; ++cpu) { + if (mBuf[cpu] != MAP_FAILED) { + // Take a snapshot of the positions + struct perf_event_mmap_page *pemp = static_cast(mBuf[cpu]); + const __u64 head = ACCESS_ONCE(pemp->data_head); - while (head > tail) { - writePos = 0; - if (!gSessionData->mLocalCapture) { - buf[writePos++] = RESPONSE_APC_DATA; + if (head + 2000 <= (unsigned int)BUF_SIZE) { + return true; + } } - // Reserve space for size - writePos += sizeof(uint32_t); - Buffer::packInt(buf, sizeof(buf), writePos, FRAME_PERF); - Buffer::packInt(buf, sizeof(buf), writePos, cpu); + } + + return false; +} + +class PerfFrame { +public: + PerfFrame(Sender *const sender) : mSender(sender), mWritePos(-1), mCpuSizePos(-1) {} + + void add(const int cpu, const __u64 head, __u64 tail, const uint8_t *const b) { + cpuHeader(cpu); while (head > tail) { const int count = reinterpret_cast(b + (tail & BUF_MASK))->size/sizeof(uint64_t); // Can this whole message be written as Streamline assumes events are not split between frames - if (sizeof(buf) <= writePos + count*Buffer::MAXSIZE_PACK64) { - break; + if (sizeof(mBuf) <= mWritePos + count*Buffer::MAXSIZE_PACK64) { + send(); + cpuHeader(cpu); } for (int i = 0; i < count; ++i) { // Must account for message size - Buffer::packInt64(buf, sizeof(buf), writePos, *reinterpret_cast(b + (tail & BUF_MASK))); + Buffer::packInt64(mBuf, sizeof(mBuf), mWritePos, *reinterpret_cast(b + (tail & BUF_MASK))); tail += sizeof(uint64_t); } } + } - // Write size - Buffer::writeLEInt(reinterpret_cast(buf + typeLength), writePos - typeLength - sizeof(uint32_t)); - sender->writeData(buf, writePos, RESPONSE_APC_DATA); + void send() { + if (mWritePos > 0) { + writeFrameSize(); + mSender->writeData(mBuf, mWritePos, RESPONSE_APC_DATA); + mWritePos = -1; + mCpuSizePos = -1; + } + } + +private: + void writeFrameSize() { + writeCpuSize(); + const int typeLength = gSessionData.mLocalCapture ? 0 : 1; + Buffer::writeLEInt(reinterpret_cast(mBuf + typeLength), mWritePos - typeLength - sizeof(uint32_t)); } -} + + void frameHeader() { + if (mWritePos < 0) { + mWritePos = 0; + mCpuSizePos = -1; + if (!gSessionData.mLocalCapture) { + mBuf[mWritePos++] = RESPONSE_APC_DATA; + } + // Reserve space for frame size + mWritePos += sizeof(uint32_t); + Buffer::packInt(mBuf, sizeof(mBuf), mWritePos, FRAME_PERF); + } + } + + void writeCpuSize() { + if (mCpuSizePos >= 0) { + Buffer::writeLEInt(reinterpret_cast(mBuf + mCpuSizePos), mWritePos - mCpuSizePos - sizeof(uint32_t)); + } + } + + void cpuHeader(const int cpu) { + if (sizeof(mBuf) <= mWritePos + Buffer::MAXSIZE_PACK32 + sizeof(uint32_t)) { + send(); + } + frameHeader(); + writeCpuSize(); + Buffer::packInt(mBuf, sizeof(mBuf), mWritePos, cpu); + mCpuSizePos = mWritePos; + // Reserve space for cpu size + mWritePos += sizeof(uint32_t); + } + + // Pick a big size but something smaller than the chunkSize in Sender::writeData which is 100k + char mBuf[1<<16]; + Sender *const mSender; + int mWritePos; + int mCpuSizePos; + + // Intentionally unimplemented + PerfFrame(const PerfFrame &); + PerfFrame& operator=(const PerfFrame &); +}; bool PerfBuffer::send(Sender *const sender) { - for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + PerfFrame frame(sender); + + for (int cpu = 0; cpu < gSessionData.mCores; ++cpu) { if (mBuf[cpu] == MAP_FAILED) { continue; } // Take a snapshot of the positions struct perf_event_mmap_page *pemp = static_cast(mBuf[cpu]); - const __u64 head = pemp->data_head; - const __u64 tail = pemp->data_tail; + const __u64 head = ACCESS_ONCE(pemp->data_head); + const __u64 tail = ACCESS_ONCE(pemp->data_tail); if (head > tail) { - const uint8_t *const b = static_cast(mBuf[cpu]) + gSessionData->mPageSize; - compressAndSend(cpu, head, tail, b, sender); + const uint8_t *const b = static_cast(mBuf[cpu]) + gSessionData.mPageSize; + frame.add(cpu, head, tail, b); // Update tail with the data read pemp->data_tail = head; } if (mDiscard[cpu]) { - munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE); + munmap(mBuf[cpu], gSessionData.mPageSize + BUF_SIZE); mBuf[cpu] = MAP_FAILED; mDiscard[cpu] = false; mFds[cpu] = -1; - logg->logMessage("%s(%s:%i): Unmaped cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu); + logg.logMessage("Unmaped cpu %i", cpu); } } + frame.send(); + return true; } diff --git a/tools/gator/daemon/PerfBuffer.h b/tools/gator/daemon/PerfBuffer.h index 25a1062..32cc062 100644 --- a/tools/gator/daemon/PerfBuffer.h +++ b/tools/gator/daemon/PerfBuffer.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -11,7 +11,7 @@ #include "Config.h" -#define BUF_SIZE (gSessionData->mTotalBufferSize * 1024 * 1024) +#define BUF_SIZE (gSessionData.mTotalBufferSize * 1024 * 1024) #define BUF_MASK (BUF_SIZE - 1) class Sender; @@ -24,6 +24,7 @@ public: bool useFd(const int cpu, const int fd); void discard(const int cpu); bool isEmpty(); + bool isFull(); bool send(Sender *const sender); private: diff --git a/tools/gator/daemon/PerfDriver.cpp b/tools/gator/daemon/PerfDriver.cpp index ee90284..2c78cbf 100644 --- a/tools/gator/daemon/PerfDriver.cpp +++ b/tools/gator/daemon/PerfDriver.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -21,65 +21,16 @@ #include "DynBuf.h" #include "Logging.h" #include "PerfGroup.h" +#include "Proc.h" #include "SessionData.h" -#include "Setup.h" #define PERF_DEVICES "/sys/bus/event_source/devices" #define TYPE_DERIVED ~0U -// From gator.h -struct gator_cpu { - const int cpuid; - // Human readable name - const char *const core_name; - // gatorfs event and Perf PMU name - const char *const pmnc_name; - const int pmnc_counters; -}; - -// From gator_main.c -static const struct gator_cpu gator_cpus[] = { - { 0xb36, "ARM1136", "ARM_ARM11", 3 }, - { 0xb56, "ARM1156", "ARM_ARM11", 3 }, - { 0xb76, "ARM1176", "ARM_ARM11", 3 }, - { 0xb02, "ARM11MPCore", "ARM_ARM11MPCore", 3 }, - { 0xc05, "Cortex-A5", "ARMv7_Cortex_A5", 2 }, - { 0xc07, "Cortex-A7", "ARMv7_Cortex_A7", 4 }, - { 0xc08, "Cortex-A8", "ARMv7_Cortex_A8", 4 }, - { 0xc09, "Cortex-A9", "ARMv7_Cortex_A9", 6 }, - { 0xc0f, "Cortex-A15", "ARMv7_Cortex_A15", 6 }, - { 0xc0e, "Cortex-A17", "ARMv7_Cortex_A17", 6 }, - { 0x00f, "Scorpion", "Scorpion", 4 }, - { 0x02d, "ScorpionMP", "ScorpionMP", 4 }, - { 0x049, "KraitSIM", "Krait", 4 }, - { 0x04d, "Krait", "Krait", 4 }, - { 0x06f, "Krait S4 Pro", "Krait", 4 }, - { 0xd03, "Cortex-A53", "ARM_Cortex-A53", 6 }, - { 0xd07, "Cortex-A57", "ARM_Cortex-A57", 6 }, - { 0xd0f, "AArch64", "ARM_AArch64", 6 }, -}; - -static const char OLD_PMU_PREFIX[] = "ARMv7 Cortex-"; -static const char NEW_PMU_PREFIX[] = "ARMv7_Cortex_"; - -struct uncore_counter { - // Perf PMU name - const char *const perfName; - // gatorfs event name - const char *const gatorName; - const int count; -}; - -static const struct uncore_counter uncore_counters[] = { - { "CCI_400", "CCI_400", 4 }, - { "CCI_400-r1", "CCI_400-r1", 4 }, - { "ccn", "ARM_CCN_5XX", 8 }, -}; - class PerfCounter : public DriverCounter { public: - PerfCounter(DriverCounter *next, const char *name, uint32_t type, uint64_t config, bool perCpu) : DriverCounter(next, name), mType(type), mCount(0), mConfig(config), mPerCpu(perCpu) {} + PerfCounter(DriverCounter *next, const char *name, uint32_t type, uint64_t config, uint64_t sampleType, uint64_t flags, const int count) : DriverCounter(next, name), mType(type), mConfig(config), mSampleType(sampleType), mFlags(flags), mCount(count) {} ~PerfCounter() { } @@ -89,13 +40,42 @@ public: void setCount(const int count) { mCount = count; } uint64_t getConfig() const { return mConfig; } void setConfig(const uint64_t config) { mConfig = config; } - bool isPerCpu() const { return mPerCpu; } + uint64_t getSampleType() const { return mSampleType; } + void setSampleType(uint64_t sampleType) { mSampleType = sampleType; } + uint64_t getFlags() const { return mFlags; } + virtual void read(Buffer *const, const int) {} private: const uint32_t mType; - int mCount; uint64_t mConfig; - bool mPerCpu; + uint64_t mSampleType; + const uint64_t mFlags; + int mCount; + + // Intentionally undefined + PerfCounter(const PerfCounter &); + PerfCounter &operator=(const PerfCounter &); +}; + +class CPUFreqDriver : public PerfCounter { +public: + CPUFreqDriver(DriverCounter *next, uint64_t id) : PerfCounter(next, strdup("Linux_power_cpu_freq"), PERF_TYPE_TRACEPOINT, id, PERF_SAMPLE_RAW, PERF_GROUP_LEADER | PERF_GROUP_PER_CPU, 1) {} + + void read(Buffer *const buffer, const int cpu) { + char buf[64]; + + snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpu%i/cpufreq/cpuinfo_cur_freq", cpu); + int64_t freq; + if (DriverSource::readInt64Driver(buf, &freq) != 0) { + freq = 0; + } + buffer->perfCounter(cpu, getKey(), 1000*freq); + } + +private: + // Intentionally undefined + CPUFreqDriver(const CPUFreqDriver &); + CPUFreqDriver &operator=(const CPUFreqDriver &); }; PerfDriver::PerfDriver() : mIsSetup(false), mLegacySupport(false) { @@ -104,31 +84,108 @@ PerfDriver::PerfDriver() : mIsSetup(false), mLegacySupport(false) { PerfDriver::~PerfDriver() { } +class PerfTracepoint { +public: + PerfTracepoint(PerfTracepoint *const next, const DriverCounter *const counter, const char *const tracepoint) : mNext(next), mCounter(counter), mTracepoint(tracepoint) {} + + PerfTracepoint *getNext() const { return mNext; } + const DriverCounter *getCounter() const { return mCounter; } + const char *getTracepoint() const { return mTracepoint; } + +private: + PerfTracepoint *const mNext; + const DriverCounter *const mCounter; + const char *const mTracepoint; + + // Intentionally undefined + PerfTracepoint(const PerfTracepoint &); + PerfTracepoint &operator=(const PerfTracepoint &); +}; + void PerfDriver::addCpuCounters(const char *const counterName, const int type, const int numCounters) { int len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1; char *name = new char[len]; snprintf(name, len, "%s_ccnt", counterName); - setCounters(new PerfCounter(getCounters(), name, type, -1, true)); + setCounters(new PerfCounter(getCounters(), name, type, -1, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU, 0)); for (int j = 0; j < numCounters; ++j) { len = snprintf(NULL, 0, "%s_cnt%d", counterName, j) + 1; name = new char[len]; snprintf(name, len, "%s_cnt%d", counterName, j); - setCounters(new PerfCounter(getCounters(), name, type, -1, true)); + setCounters(new PerfCounter(getCounters(), name, type, -1, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU, 0)); } } -void PerfDriver::addUncoreCounters(const char *const counterName, const int type, const int numCounters) { - int len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1; - char *name = new char[len]; - snprintf(name, len, "%s_ccnt", counterName); - setCounters(new PerfCounter(getCounters(), name, type, -1, false)); +void PerfDriver::addUncoreCounters(const char *const counterName, const int type, const int numCounters, const bool hasCyclesCounter) { + int len; + char *name; + + if (hasCyclesCounter) { + len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1; + name = new char[len]; + snprintf(name, len, "%s_ccnt", counterName); + setCounters(new PerfCounter(getCounters(), name, type, -1, PERF_SAMPLE_READ, 0, 0)); + } for (int j = 0; j < numCounters; ++j) { len = snprintf(NULL, 0, "%s_cnt%d", counterName, j) + 1; name = new char[len]; snprintf(name, len, "%s_cnt%d", counterName, j); - setCounters(new PerfCounter(getCounters(), name, type, -1, false)); + setCounters(new PerfCounter(getCounters(), name, type, -1, PERF_SAMPLE_READ, 0, 0)); + } +} + +long long PerfDriver::getTracepointId(const char *const counter, const char *const name, DynBuf *const printb) { + long long result = PerfDriver::getTracepointId(name, printb); + if (result <= 0) { + logg.logSetup("%s Disabled\n%s was not found", counter, printb->getBuf()); + } + return result; +} + +void PerfDriver::readEvents(mxml_node_t *const xml) { + mxml_node_t *node = xml; + DynBuf printb; + + // Only for use with perf + if (!isSetup()) { + return; + } + + while (true) { + node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND); + if (node == NULL) { + break; + } + const char *counter = mxmlElementGetAttr(node, "counter"); + if (counter == NULL) { + continue; + } + + if (strncmp(counter, "ftrace_", 7) != 0) { + continue; + } + + const char *tracepoint = mxmlElementGetAttr(node, "tracepoint"); + if (tracepoint == NULL) { + const char *regex = mxmlElementGetAttr(node, "regex"); + if (regex == NULL) { + logg.logError("The tracepoint counter %s is missing the required tracepoint attribute", counter); + handleException(); + } else { + logg.logMessage("Not using perf for counter %s", counter); + continue; + } + } + + const char *arg = mxmlElementGetAttr(node, "arg"); + + long long id = getTracepointId(counter, tracepoint, &printb); + if (id >= 0) { + logg.logMessage("Using perf for %s", counter); + setCounters(new PerfCounter(getCounters(), strdup(counter), PERF_TYPE_TRACEPOINT, id, arg == NULL ? 0 : PERF_SAMPLE_RAW, PERF_GROUP_LEADER | PERF_GROUP_PER_CPU, 1)); + mTracepoints = new PerfTracepoint(mTracepoints, getCounters(), strdup(tracepoint)); + } } } @@ -136,18 +193,20 @@ bool PerfDriver::setup() { // Check the kernel version int release[3]; if (!getLinuxVersion(release)) { - logg->logMessage("%s(%s:%i): getLinuxVersion failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("getLinuxVersion failed"); return false; } - if (KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 4, 0)) { - logg->logMessage("%s(%s:%i): Unsupported kernel version", __FUNCTION__, __FILE__, __LINE__); + const int kernelVersion = KERNEL_VERSION(release[0], release[1], release[2]); + if (kernelVersion < KERNEL_VERSION(3, 4, 0)) { + logg.logSetup("Unsupported kernel version\nPlease upgrade to 3.4 or later"); return false; } - mLegacySupport = KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 12, 0); + mLegacySupport = kernelVersion < KERNEL_VERSION(3, 12, 0); + mClockidSupport = kernelVersion >= KERNEL_VERSION(4, 2, 0); if (access(EVENTS_PATH, R_OK) != 0) { - logg->logMessage("%s(%s:%i): " EVENTS_PATH " does not exist, is CONFIG_TRACING and CONFIG_CONTEXT_SWITCH_TRACER enabled?", __FUNCTION__, __FILE__, __LINE__); + logg.logSetup(EVENTS_PATH " does not exist\nIs CONFIG_TRACING and CONFIG_CONTEXT_SWITCH_TRACER enabled?"); return false; } @@ -155,95 +214,83 @@ bool PerfDriver::setup() { bool foundCpu = false; DIR *dir = opendir(PERF_DEVICES); if (dir == NULL) { - logg->logMessage("%s(%s:%i): opendif failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("opendir failed"); return false; } struct dirent *dirent; while ((dirent = readdir(dir)) != NULL) { - for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) { - const struct gator_cpu *const gator_cpu = &gator_cpus[i]; - - // Do the names match exactly? - if (strcasecmp(gator_cpu->pmnc_name, dirent->d_name) != 0 && - // Do these names match but have the old vs new prefix? - ((strncasecmp(dirent->d_name, OLD_PMU_PREFIX, sizeof(OLD_PMU_PREFIX) - 1) != 0 || - strncasecmp(gator_cpu->pmnc_name, NEW_PMU_PREFIX, sizeof(NEW_PMU_PREFIX) - 1) != 0 || - strcasecmp(dirent->d_name + sizeof(OLD_PMU_PREFIX) - 1, gator_cpu->pmnc_name + sizeof(NEW_PMU_PREFIX) - 1) != 0))) { - continue; - } - + logg.logMessage("perf pmu: %s", dirent->d_name); + GatorCpu *gatorCpu = GatorCpu::find(dirent->d_name); + if (gatorCpu != NULL) { int type; char buf[256]; snprintf(buf, sizeof(buf), PERF_DEVICES "/%s/type", dirent->d_name); - if (DriverSource::readIntDriver(buf, &type) != 0) { + if (DriverSource::readIntDriver(buf, &type) == 0) { + foundCpu = true; + logg.logMessage("Adding cpu counters for %s with type %i", gatorCpu->getCoreName(), type); + addCpuCounters(gatorCpu->getPmncName(), type, gatorCpu->getPmncCounters()); continue; } - - foundCpu = true; - logg->logMessage("Adding cpu counters for %s", gator_cpu->pmnc_name); - addCpuCounters(gator_cpu->pmnc_name, type, gator_cpu->pmnc_counters); } - for (int i = 0; i < ARRAY_LENGTH(uncore_counters); ++i) { - if (strcmp(dirent->d_name, uncore_counters[i].perfName) != 0) { - continue; - } - + UncorePmu *uncorePmu = UncorePmu::find(dirent->d_name); + if (uncorePmu != NULL) { int type; char buf[256]; snprintf(buf, sizeof(buf), PERF_DEVICES "/%s/type", dirent->d_name); - if (DriverSource::readIntDriver(buf, &type) != 0) { + if (DriverSource::readIntDriver(buf, &type) == 0) { + logg.logMessage("Adding uncore counters for %s with type %i", uncorePmu->getCoreName(), type); + addUncoreCounters(uncorePmu->getCoreName(), type, uncorePmu->getPmncCounters(), uncorePmu->getHasCyclesCounter()); continue; } - - logg->logMessage("Adding uncore counters for %s", uncore_counters[i].gatorName); - addUncoreCounters(uncore_counters[i].gatorName, type, uncore_counters[i].count); } } closedir(dir); if (!foundCpu) { - // If no cpu was found based on pmu names, try by cpuid - for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) { - if (gSessionData->mMaxCpuId != gator_cpus[i].cpuid) { - continue; - } - + GatorCpu *gatorCpu = GatorCpu::find(gSessionData.mMaxCpuId); + if (gatorCpu != NULL) { foundCpu = true; - logg->logMessage("Adding cpu counters (based on cpuid) for %s", gator_cpus[i].pmnc_name); - addCpuCounters(gator_cpus[i].pmnc_name, PERF_TYPE_RAW, gator_cpus[i].pmnc_counters); + logg.logMessage("Adding cpu counters (based on cpuid) for %s", gatorCpu->getCoreName()); + addCpuCounters(gatorCpu->getPmncName(), PERF_TYPE_RAW, gatorCpu->getPmncCounters()); } } - /* if (!foundCpu) { - // If all else fails, use the perf architected counters - // 9 because that's how many are in events-Perf-Hardware.xml - assume they can all be enabled at once - addCpuCounters("Perf_Hardware", PERF_TYPE_HARDWARE, 9); + logCpuNotFound(); +#if defined(__arm__) || defined(__aarch64__) + addCpuCounters("Other", PERF_TYPE_RAW, 6); +#endif } - */ // Add supported software counters long long id; DynBuf printb; - id = getTracepointId("irq/softirq_exit", &printb); + id = getTracepointId("Linux_irq_softirq", "irq/softirq_exit", &printb); if (id >= 0) { - setCounters(new PerfCounter(getCounters(), "Linux_irq_softirq", PERF_TYPE_TRACEPOINT, id, true)); + setCounters(new PerfCounter(getCounters(), strdup("Linux_irq_softirq"), PERF_TYPE_TRACEPOINT, id, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU, 0)); } - id = getTracepointId("irq/irq_handler_exit", &printb); + id = getTracepointId("Linux_irq_irq", "irq/irq_handler_exit", &printb); if (id >= 0) { - setCounters(new PerfCounter(getCounters(), "Linux_irq_irq", PERF_TYPE_TRACEPOINT, id, true)); + setCounters(new PerfCounter(getCounters(), strdup("Linux_irq_irq"), PERF_TYPE_TRACEPOINT, id, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU, 0)); } - id = getTracepointId(SCHED_SWITCH, &printb); + id = getTracepointId("Linux_sched_switch", SCHED_SWITCH, &printb); if (id >= 0) { - setCounters(new PerfCounter(getCounters(), "Linux_sched_switch", PERF_TYPE_TRACEPOINT, id, true)); + setCounters(new PerfCounter(getCounters(), strdup("Linux_sched_switch"), PERF_TYPE_TRACEPOINT, id, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU, 0)); + } + + id = getTracepointId("Linux_power_cpu_freq", CPU_FREQUENCY, &printb); + if (id >= 0 && access("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq", R_OK) == 0) { + setCounters(new CPUFreqDriver(getCounters(), id)); } - setCounters(new PerfCounter(getCounters(), "Linux_cpu_wait_contention", TYPE_DERIVED, -1, false)); + setCounters(new PerfCounter(getCounters(), strdup("Linux_cpu_wait_contention"), TYPE_DERIVED, -1, 0, 0, 0)); + setCounters(new PerfCounter(getCounters(), strdup("Linux_cpu_system"), TYPE_DERIVED, -1, 0, 0, 0)); + setCounters(new PerfCounter(getCounters(), strdup("Linux_cpu_user"), TYPE_DERIVED, -1, 0, 0, 0)); //Linux_cpu_wait_io @@ -251,58 +298,69 @@ bool PerfDriver::setup() { return true; } +void logCpuNotFound() { +#if defined(__arm__) || defined(__aarch64__) + logg.logSetup("CPU is not recognized\nUsing the ARM architected counters"); +#else + logg.logSetup("CPU is not recognized\nOmitting CPU counters"); +#endif +} + bool PerfDriver::summary(Buffer *const buffer) { struct utsname utsname; if (uname(&utsname) != 0) { - logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("uname failed"); return false; } char buf[512]; snprintf(buf, sizeof(buf), "%s %s %s %s %s GNU/Linux", utsname.sysname, utsname.nodename, utsname.release, utsname.version, utsname.machine); + long pageSize = sysconf(_SC_PAGESIZE); + if (pageSize < 0) { + logg.logMessage("sysconf _SC_PAGESIZE failed"); + return false; + } + struct timespec ts; if (clock_gettime(CLOCK_REALTIME, &ts) != 0) { - logg->logMessage("%s(%s:%i): clock_gettime failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("clock_gettime failed"); return false; } const int64_t timestamp = (int64_t)ts.tv_sec * NS_PER_S + ts.tv_nsec; const uint64_t monotonicStarted = getTime(); - gSessionData->mMonotonicStarted = monotonicStarted; + gSessionData.mMonotonicStarted = monotonicStarted; + const uint64_t currTime = 0;//getTime() - gSessionData.mMonotonicStarted; - buffer->summary(monotonicStarted, timestamp, monotonicStarted, monotonicStarted, buf); + buffer->summary(currTime, timestamp, monotonicStarted, monotonicStarted, buf, pageSize, getClockidSupport()); - for (int i = 0; i < gSessionData->mCores; ++i) { - coreName(monotonicStarted, buffer, i); + for (int i = 0; i < gSessionData.mCores; ++i) { + coreName(currTime, buffer, i); } - buffer->commit(monotonicStarted); + buffer->commit(currTime); return true; } -void PerfDriver::coreName(const uint32_t startTime, Buffer *const buffer, const int cpu) { +void PerfDriver::coreName(const uint64_t currTime, Buffer *const buffer, const int cpu) { + const SharedData *const sharedData = gSessionData.mSharedData; // Don't send information on a cpu we know nothing about - if (gSessionData->mCpuIds[cpu] == -1) { + if (sharedData->mCpuIds[cpu] == -1) { return; } - int j; - for (j = 0; j < ARRAY_LENGTH(gator_cpus); ++j) { - if (gator_cpus[j].cpuid == gSessionData->mCpuIds[cpu]) { - break; - } - } - if (gator_cpus[j].cpuid == gSessionData->mCpuIds[cpu]) { - buffer->coreName(startTime, cpu, gSessionData->mCpuIds[cpu], gator_cpus[j].core_name); + GatorCpu *gatorCpu = GatorCpu::find(sharedData->mCpuIds[cpu]); + if (gatorCpu != NULL && gatorCpu->getCpuid() == sharedData->mCpuIds[cpu]) { + buffer->coreName(currTime, cpu, sharedData->mCpuIds[cpu], gatorCpu->getCoreName()); } else { char buf[32]; - if (gSessionData->mCpuIds[cpu] == -1) { + if (sharedData->mCpuIds[cpu] == -1) { snprintf(buf, sizeof(buf), "Unknown"); } else { - snprintf(buf, sizeof(buf), "Unknown (0x%.3x)", gSessionData->mCpuIds[cpu]); + snprintf(buf, sizeof(buf), "Unknown (0x%.3x)", sharedData->mCpuIds[cpu]); } - buffer->coreName(startTime, cpu, gSessionData->mCpuIds[cpu], buf); + buffer->coreName(currTime, cpu, sharedData->mCpuIds[cpu], buf); } } @@ -317,18 +375,49 @@ void PerfDriver::setupCounter(Counter &counter) { if (counter.getEvent() != -1) { perfCounter->setConfig(counter.getEvent()); } - perfCounter->setCount(counter.getCount()); + if (counter.getCount() > 0) { + // EBS + perfCounter->setCount(counter.getCount()); + // Collect samples + perfCounter->setSampleType(perfCounter->getSampleType() | PERF_SAMPLE_TID | PERF_SAMPLE_IP); + } perfCounter->setEnabled(true); counter.setKey(perfCounter->getKey()); } bool PerfDriver::enable(const uint64_t currTime, PerfGroup *const group, Buffer *const buffer) const { for (PerfCounter *counter = static_cast(getCounters()); counter != NULL; counter = static_cast(counter->getNext())) { - if (counter->isEnabled() && (counter->getType() != TYPE_DERIVED)) { - if (!group->add(currTime, buffer, counter->getKey(), counter->getType(), counter->getConfig(), counter->getCount(), counter->getCount() > 0 ? PERF_SAMPLE_TID | PERF_SAMPLE_IP : 0, counter->isPerCpu() ? PERF_GROUP_PER_CPU : 0)) { - logg->logMessage("%s(%s:%i): PerfGroup::add failed", __FUNCTION__, __FILE__, __LINE__); - return false; - } + if (counter->isEnabled() && (counter->getType() != TYPE_DERIVED) && + !group->add(currTime, buffer, counter->getKey(), counter->getType(), counter->getConfig(), counter->getCount(), counter->getSampleType(), counter->getFlags())) { + logg.logMessage("PerfGroup::add failed"); + return false; + } + } + + return true; +} + +void PerfDriver::read(Buffer *const buffer, const int cpu) { + for (PerfCounter *counter = static_cast(getCounters()); counter != NULL; counter = static_cast(counter->getNext())) { + if (!counter->isEnabled()) { + continue; + } + counter->read(buffer, cpu); + } +} + +bool PerfDriver::sendTracepointFormats(const uint64_t currTime, Buffer *const buffer, DynBuf *const printb, DynBuf *const b) { + if ( + !readTracepointFormat(currTime, buffer, SCHED_SWITCH, printb, b) || + !readTracepointFormat(currTime, buffer, CPU_IDLE, printb, b) || + !readTracepointFormat(currTime, buffer, CPU_FREQUENCY, printb, b) || + false) { + return false; + } + + for (PerfTracepoint *tracepoint = mTracepoints; tracepoint != NULL; tracepoint = tracepoint->getNext()) { + if (tracepoint->getCounter()->isEnabled() && !readTracepointFormat(currTime, buffer, tracepoint->getTracepoint(), printb, b)) { + return false; } } @@ -337,13 +426,13 @@ bool PerfDriver::enable(const uint64_t currTime, PerfGroup *const group, Buffer long long PerfDriver::getTracepointId(const char *const name, DynBuf *const printb) { if (!printb->printf(EVENTS_PATH "/%s/id", name)) { - logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("DynBuf::printf failed"); return -1; } int64_t result; if (DriverSource::readInt64Driver(printb->getBuf(), &result) != 0) { - logg->logMessage("%s(%s:%i): DriverSource::readInt64Driver failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("Unable to read tracepoint id for %s", printb->getBuf()); return -1; } diff --git a/tools/gator/daemon/PerfDriver.h b/tools/gator/daemon/PerfDriver.h index 846203a..c6d8126 100644 --- a/tools/gator/daemon/PerfDriver.h +++ b/tools/gator/daemon/PerfDriver.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -13,16 +13,14 @@ #include "Driver.h" -// If debugfs is not mounted at /sys/kernel/debug, update DEBUGFS_PATH -#define DEBUGFS_PATH "/sys/kernel/debug" -#define EVENTS_PATH DEBUGFS_PATH "/tracing/events" - #define SCHED_SWITCH "sched/sched_switch" #define CPU_IDLE "power/cpu_idle" +#define CPU_FREQUENCY "power/cpu_frequency" class Buffer; class DynBuf; class PerfGroup; +class PerfTracepoint; class PerfDriver : public SimpleDriver { public: @@ -30,24 +28,32 @@ public: ~PerfDriver(); bool getLegacySupport() const { return mLegacySupport; } + bool getClockidSupport() const { return mClockidSupport; } + void readEvents(mxml_node_t *const xml); bool setup(); bool summary(Buffer *const buffer); - void coreName(const uint32_t startTime, Buffer *const buffer, const int cpu); + void coreName(const uint64_t currTime, Buffer *const buffer, const int cpu); bool isSetup() const { return mIsSetup; } void setupCounter(Counter &counter); bool enable(const uint64_t currTime, PerfGroup *const group, Buffer *const buffer) const; + void read(Buffer *const buffer, const int cpu); + bool sendTracepointFormats(const uint64_t currTime, Buffer *const buffer, DynBuf *const printb, DynBuf *const b); static long long getTracepointId(const char *const name, DynBuf *const printb); + static long long getTracepointId(const char *const counter, const char *const name, DynBuf *const printb); private: void addCpuCounters(const char *const counterName, const int type, const int numCounters); - void addUncoreCounters(const char *const counterName, const int type, const int numCounters); + void addUncoreCounters(const char *const counterName, const int type, const int numCounters, const bool hasCyclesCounter); - bool mIsSetup; - bool mLegacySupport; + int mIsSetup : 1, + mLegacySupport : 1, + mClockidSupport : 1, + mUnused0 : 29; + PerfTracepoint *mTracepoints; // Intentionally undefined PerfDriver(const PerfDriver &); diff --git a/tools/gator/daemon/PerfGroup.cpp b/tools/gator/daemon/PerfGroup.cpp index 4fd960a..25999e5 100644 --- a/tools/gator/daemon/PerfGroup.cpp +++ b/tools/gator/daemon/PerfGroup.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -10,23 +10,27 @@ #include #include +#include #include #include #include #include #include "Buffer.h" +#include "DynBuf.h" #include "Logging.h" #include "Monitor.h" #include "PerfBuffer.h" #include "SessionData.h" +static const int schedSwitchKey = getEventKey(); + #define DEFAULT_PEA_ARGS(pea, additionalSampleType) \ pea.size = sizeof(pea); \ /* Emit time, read_format below, group leader id, and raw tracepoint info */ \ - pea.sample_type = (gSessionData->perf.getLegacySupport() \ - ? PERF_SAMPLE_TID | PERF_SAMPLE_IP | PERF_SAMPLE_TIME | PERF_SAMPLE_READ | PERF_SAMPLE_ID \ - : PERF_SAMPLE_TIME | PERF_SAMPLE_READ | PERF_SAMPLE_IDENTIFIER ) | additionalSampleType; \ + pea.sample_type = (gSessionData.mPerf.getLegacySupport() \ + ? PERF_SAMPLE_TID | PERF_SAMPLE_IP | PERF_SAMPLE_ID \ + : PERF_SAMPLE_IDENTIFIER ) | PERF_SAMPLE_TIME | additionalSampleType; \ /* Emit emit value in group format */ \ pea.read_format = PERF_FORMAT_ID | PERF_FORMAT_GROUP; \ /* start out disabled */ \ @@ -34,7 +38,10 @@ /* have a sampling interrupt happen when we cross the wakeup_watermark boundary */ \ pea.watermark = 1; \ /* Be conservative in flush size as only one buffer set is monitored */ \ - pea.wakeup_watermark = BUF_SIZE / 2 + pea.wakeup_watermark = BUF_SIZE / 2; \ + /* Use the monotonic raw clock if possible */ \ + pea.use_clockid = gSessionData.mPerf.getClockidSupport() ? 1 : 0; \ + pea.clockid = gSessionData.mPerf.getClockidSupport() ? CLOCK_MONOTONIC_RAW : 0 static int sys_perf_event_open(struct perf_event_attr *const attr, const pid_t pid, const int cpu, const int group_fd, const unsigned long flags) { int fd = syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); @@ -49,11 +56,12 @@ static int sys_perf_event_open(struct perf_event_attr *const attr, const pid_t p return fd; } -PerfGroup::PerfGroup(PerfBuffer *const pb) : mPb(pb) { +PerfGroup::PerfGroup(PerfBuffer *const pb) : mPb(pb), mSchedSwitchId(-1) { memset(&mAttrs, 0, sizeof(mAttrs)); - memset(&mPerCpu, 0, sizeof(mPerCpu)); + memset(&mFlags, 0, sizeof(mFlags)); memset(&mKeys, -1, sizeof(mKeys)); memset(&mFds, -1, sizeof(mFds)); + memset(&mLeaders, -1, sizeof(mLeaders)); } PerfGroup::~PerfGroup() { @@ -64,7 +72,7 @@ PerfGroup::~PerfGroup() { } } -bool PerfGroup::add(const uint64_t currTime, Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags) { +int PerfGroup::doAdd(const uint64_t currTime, Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags) { int i; for (i = 0; i < ARRAY_LENGTH(mKeys); ++i) { if (mKeys[i] < 0) { @@ -73,8 +81,8 @@ bool PerfGroup::add(const uint64_t currTime, Buffer *const buffer, const int key } if (i >= ARRAY_LENGTH(mKeys)) { - logg->logMessage("%s(%s:%i): Too many counters", __FUNCTION__, __FILE__, __LINE__); - return false; + logg.logMessage("Too many counters"); + return -1; } DEFAULT_PEA_ARGS(mAttrs[i], sampleType); @@ -82,121 +90,225 @@ bool PerfGroup::add(const uint64_t currTime, Buffer *const buffer, const int key mAttrs[i].config = config; mAttrs[i].sample_period = sample; // always be on the CPU but only a group leader can be pinned - mAttrs[i].pinned = (i == 0 ? 1 : 0); + mAttrs[i].pinned = (flags & PERF_GROUP_LEADER ? 1 : 0); mAttrs[i].mmap = (flags & PERF_GROUP_MMAP ? 1 : 0); mAttrs[i].comm = (flags & PERF_GROUP_COMM ? 1 : 0); mAttrs[i].freq = (flags & PERF_GROUP_FREQ ? 1 : 0); mAttrs[i].task = (flags & PERF_GROUP_TASK ? 1 : 0); mAttrs[i].sample_id_all = (flags & PERF_GROUP_SAMPLE_ID_ALL ? 1 : 0); - mPerCpu[i] = (flags & PERF_GROUP_PER_CPU); + mFlags[i] = flags; mKeys[i] = key; - buffer->pea(currTime, &mAttrs[i], key); + buffer->marshalPea(currTime, &mAttrs[i], key); + + return i; +} + +/* Counters from different hardware PMUs need to be in different + * groups. Software counters can be in the same group as the CPU and + * should be marked as PERF_GROUP_CPU. The big and little clusters can + * be in the same group as only one or the other will be available on + * a given CPU. + */ +int PerfGroup::getEffectiveType(const int type, const int flags) { + const int effectiveType = flags & PERF_GROUP_CPU ? (int)PERF_TYPE_HARDWARE : type; + if (effectiveType >= ARRAY_LENGTH(mLeaders)) { + logg.logError("perf type is too large, please increase the size of PerfGroup::mLeaders"); + handleException(); + } + return effectiveType; +} + +bool PerfGroup::createCpuGroup(const uint64_t currTime, Buffer *const buffer) { + if (mSchedSwitchId < 0) { + DynBuf b; + mSchedSwitchId = PerfDriver::getTracepointId(SCHED_SWITCH, &b); + if (mSchedSwitchId < 0) { + logg.logMessage("Unable to read sched_switch id"); + return false; + } + } + + mLeaders[PERF_TYPE_HARDWARE] = doAdd(currTime, buffer, schedSwitchKey, PERF_TYPE_TRACEPOINT, mSchedSwitchId, 1, PERF_SAMPLE_READ | PERF_SAMPLE_RAW, PERF_GROUP_MMAP | PERF_GROUP_COMM | PERF_GROUP_TASK | PERF_GROUP_SAMPLE_ID_ALL | PERF_GROUP_PER_CPU | PERF_GROUP_LEADER | PERF_GROUP_CPU); + if (mLeaders[PERF_TYPE_HARDWARE] < 0) { + return false; + } + + if (gSessionData.mSampleRate > 0 && !gSessionData.mIsEBS && doAdd(currTime, buffer, INT_MAX-PERF_TYPE_HARDWARE, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, 1000000000UL / gSessionData.mSampleRate, PERF_SAMPLE_TID | PERF_SAMPLE_IP | PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU) < 0) { + return false; + } return true; } +bool PerfGroup::add(const uint64_t currTime, Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags) { + const int effectiveType = getEffectiveType(type, flags); + + // Does a group exist for this already? + if (!(flags & PERF_GROUP_LEADER) && mLeaders[effectiveType] < 0) { + // Create it + if (effectiveType == PERF_TYPE_HARDWARE) { + if (!createCpuGroup(currTime, buffer)) { + return false; + } + } else { + // Non-CPU PMUs are sampled every 100ms for Sample Rate: None and EBS, otherwise they would never be sampled + const uint64_t timeout = gSessionData.mSampleRate > 0 && !gSessionData.mIsEBS ? 1000000000UL / gSessionData.mSampleRate : 100000000UL; + mLeaders[effectiveType] = doAdd(currTime, buffer, INT_MAX-effectiveType, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, timeout, PERF_SAMPLE_READ, PERF_GROUP_LEADER); + if (mLeaders[effectiveType] < 0) { + return false; + } + } + } + + if (!(flags & PERF_GROUP_LEADER) && effectiveType != PERF_TYPE_HARDWARE && (flags & PERF_GROUP_PER_CPU)) { + logg.logError("'uncore' counters are not permitted to be per-cpu"); + handleException(); + } + + return doAdd(currTime, buffer, key, type, config, sample, sampleType, flags) >= 0; +} + int PerfGroup::prepareCPU(const int cpu, Monitor *const monitor) { - logg->logMessage("%s(%s:%i): Onlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu); + logg.logMessage("Onlining cpu %i", cpu); for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { if (mKeys[i] < 0) { continue; } - if ((cpu != 0) && !mPerCpu[i]) { + if ((cpu != 0) && !(mFlags[i] & PERF_GROUP_PER_CPU)) { continue; } - const int offset = i * gSessionData->mCores; - if (mFds[cpu + offset] >= 0) { - logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__); + const int offset = i * gSessionData.mCores + cpu; + if (mFds[offset] >= 0) { + logg.logMessage("cpu already online or not correctly cleaned up"); return PG_FAILURE; } - logg->logMessage("%s(%s:%i): perf_event_open cpu: %i type: %lli config: %lli sample: %lli sample_type: 0x%llx pinned: %i mmap: %i comm: %i freq: %i task: %i sample_id_all: %i", __FUNCTION__, __FILE__, __LINE__, cpu, (long long)mAttrs[i].type, (long long)mAttrs[i].config, (long long)mAttrs[i].sample_period, (long long)mAttrs[i].sample_type, mAttrs[i].pinned, mAttrs[i].mmap, mAttrs[i].comm, mAttrs[i].freq, mAttrs[i].task, mAttrs[i].sample_id_all); - mFds[cpu + offset] = sys_perf_event_open(&mAttrs[i], -1, cpu, i == 0 ? -1 : mFds[cpu], i == 0 ? 0 : PERF_FLAG_FD_OUTPUT); - if (mFds[cpu + offset] < 0) { - logg->logMessage("%s(%s:%i): failed %s", __FUNCTION__, __FILE__, __LINE__, strerror(errno)); + logg.logMessage("perf_event_open cpu: %i type: %i config: %lli sample: %lli sample_type: 0x%llx pinned: %lli mmap: %lli comm: %lli freq: %lli task: %lli sample_id_all: %lli", cpu, mAttrs[i].type, mAttrs[i].config, mAttrs[i].sample_period, mAttrs[i].sample_type, mAttrs[i].pinned, mAttrs[i].mmap, mAttrs[i].comm, mAttrs[i].freq, mAttrs[i].task, mAttrs[i].sample_id_all); + mFds[offset] = sys_perf_event_open(&mAttrs[i], -1, cpu, mAttrs[i].pinned ? -1 : mFds[mLeaders[getEffectiveType(mAttrs[i].type, mFlags[i])] * gSessionData.mCores + cpu], mAttrs[i].pinned ? 0 : PERF_FLAG_FD_OUTPUT); + if (mFds[offset] < 0) { + logg.logMessage("failed %s", strerror(errno)); if (errno == ENODEV) { + // The core is offline return PG_CPU_OFFLINE; } - continue; + if (errno == ENOENT) { + // This event doesn't apply to this CPU but should apply to a different one, ex bL + continue; + } + logg.logMessage("perf_event_open failed"); + return PG_FAILURE; } - if (!mPb->useFd(cpu, mFds[cpu + offset])) { - logg->logMessage("%s(%s:%i): PerfBuffer::useFd failed", __FUNCTION__, __FILE__, __LINE__); + if (!mPb->useFd(cpu, mFds[offset])) { + logg.logMessage("PerfBuffer::useFd failed"); return PG_FAILURE; } - if (!monitor->add(mFds[cpu + offset])) { - logg->logMessage("%s(%s:%i): Monitor::add failed", __FUNCTION__, __FILE__, __LINE__); - return PG_FAILURE; + if (!monitor->add(mFds[offset])) { + logg.logMessage("Monitor::add failed"); + return PG_FAILURE; } } return PG_SUCCESS; } -int PerfGroup::onlineCPU(const uint64_t currTime, const int cpu, const bool start, Buffer *const buffer) { - __u64 ids[ARRAY_LENGTH(mKeys)]; - int coreKeys[ARRAY_LENGTH(mKeys)]; - int idCount = 0; +static bool readAndSend(const uint64_t currTime, Buffer *const buffer, const int fd, const int keyCount, const int *const keys) { + char buf[1024]; + ssize_t bytes = read(fd, buf, sizeof(buf)); + if (bytes < 0) { + logg.logMessage("read failed"); + return false; + } + buffer->marshalKeysOld(currTime, keyCount, keys, bytes, buf); - for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { - const int fd = mFds[cpu + i * gSessionData->mCores]; - if (fd < 0) { - continue; - } + return true; +} - coreKeys[idCount] = mKeys[i]; - if (!gSessionData->perf.getLegacySupport() && ioctl(fd, PERF_EVENT_IOC_ID, &ids[idCount]) != 0 && - // Workaround for running 32-bit gatord on 64-bit systems, kernel patch in the works - ioctl(fd, (PERF_EVENT_IOC_ID & ~IOCSIZE_MASK) | (8 << _IOC_SIZESHIFT), &ids[idCount]) != 0) { - logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); - return 0; +int PerfGroup::onlineCPU(const uint64_t currTime, const int cpu, const bool enable, Buffer *const buffer) { + bool addedEvents = false; + + if (!gSessionData.mPerf.getLegacySupport()) { + int idCount = 0; + int coreKeys[ARRAY_LENGTH(mKeys)]; + __u64 ids[ARRAY_LENGTH(mKeys)]; + + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + const int fd = mFds[cpu + i * gSessionData.mCores]; + if (fd < 0) { + continue; + } + + coreKeys[idCount] = mKeys[i]; + if (ioctl(fd, PERF_EVENT_IOC_ID, &ids[idCount]) != 0 && + // Workaround for running 32-bit gatord on 64-bit systems, kernel patch in the works + ioctl(fd, (PERF_EVENT_IOC_ID & ~IOCSIZE_MASK) | (8 << _IOC_SIZESHIFT), &ids[idCount]) != 0) { + logg.logMessage("ioctl failed"); + return 0; + } + ++idCount; + addedEvents = true; } - ++idCount; - } - if (!gSessionData->perf.getLegacySupport()) { - buffer->keys(currTime, idCount, ids, coreKeys); + buffer->marshalKeys(currTime, idCount, ids, coreKeys); } else { - char buf[1024]; - ssize_t bytes = read(mFds[cpu], buf, sizeof(buf)); - if (bytes < 0) { - logg->logMessage("read failed"); - return 0; + int idCounts[ARRAY_LENGTH(mLeaders)] = { 0 }; + int coreKeys[ARRAY_LENGTH(mLeaders)][ARRAY_LENGTH(mKeys)]; + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + const int fd = mFds[cpu + i * gSessionData.mCores]; + if (fd < 0) { + continue; + } + + const int effectiveType = getEffectiveType(mAttrs[i].type, mFlags[i]); + if (mAttrs[i].pinned && mLeaders[effectiveType] != i) { + if (!readAndSend(currTime, buffer, fd, 1, mKeys + i)) { + return 0; + } + } else { + coreKeys[effectiveType][idCounts[effectiveType]] = mKeys[i]; + ++idCounts[effectiveType]; + addedEvents = true; + } + } + + for (int i = 0; i < ARRAY_LENGTH(mLeaders); ++i) { + if (idCounts[i] > 0 && !readAndSend(currTime, buffer, mFds[mLeaders[i] * gSessionData.mCores + cpu], idCounts[i], coreKeys[i])) { + return 0; + } } - buffer->keysOld(currTime, idCount, coreKeys, bytes, buf); } - if (start) { + if (enable) { for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { - int offset = i * gSessionData->mCores + cpu; - if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_ENABLE, 0) < 0) { - logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + int offset = i * gSessionData.mCores + cpu; + if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_ENABLE, 0) != 0) { + logg.logMessage("ioctl failed"); return 0; } } } - if (idCount == 0) { - logg->logMessage("%s(%s:%i): no events came online", __FUNCTION__, __FILE__, __LINE__); + if (!addedEvents) { + logg.logMessage("no events came online"); } - return idCount; + return 1; } bool PerfGroup::offlineCPU(const int cpu) { - logg->logMessage("%s(%s:%i): Offlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu); + logg.logMessage("Offlining cpu %i", cpu); - for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { - int offset = i * gSessionData->mCores + cpu; - if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_DISABLE, 0) < 0) { - logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + for (int i = ARRAY_LENGTH(mKeys) - 1; i >= 0; --i) { + int offset = i * gSessionData.mCores + cpu; + if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_DISABLE, 0) != 0) { + logg.logMessage("ioctl failed"); return false; } } @@ -204,12 +316,12 @@ bool PerfGroup::offlineCPU(const int cpu) { // Mark the buffer so that it will be released next time it's read mPb->discard(cpu); - for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + for (int i = ARRAY_LENGTH(mKeys) - 1; i >= 0; --i) { if (mKeys[i] < 0) { continue; } - int offset = i * gSessionData->mCores + cpu; + int offset = i * gSessionData.mCores + cpu; if (mFds[offset] >= 0) { close(mFds[offset]); mFds[offset] = -1; @@ -219,20 +331,28 @@ bool PerfGroup::offlineCPU(const int cpu) { return true; } -bool PerfGroup::start() { +void PerfGroup::start() { + char buf[1<<10]; + for (int pos = 0; pos < ARRAY_LENGTH(mFds); ++pos) { - if (mFds[pos] >= 0 && ioctl(mFds[pos], PERF_EVENT_IOC_ENABLE, 0) < 0) { - logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); - goto fail; + if (mFds[pos] >= 0 && ioctl(mFds[pos], PERF_EVENT_IOC_ENABLE, 0) != 0) { + logg.logError("Unable to enable a perf event"); + handleException(); } } - return true; - - fail: - stop(); - - return false; + // Try reading from all the group leaders to ensure that the event isn't disabled + for (int pos = 0; pos < ARRAY_LENGTH(mAttrs); ++pos) { + if (mAttrs[pos].pinned) { + for (int cpu = 0; cpu < gSessionData.mCores; ++cpu) { + int fd = mFds[pos * gSessionData.mCores + cpu]; + if (fd >= 0 && read(fd, buf, sizeof(buf)) <= 0) { + logg.logError("Unable to read all perf groups, perhaps too many events were enabled"); + handleException(); + } + } + } + } } void PerfGroup::stop() { diff --git a/tools/gator/daemon/PerfGroup.h b/tools/gator/daemon/PerfGroup.h index f7b3d72..13a18c3 100644 --- a/tools/gator/daemon/PerfGroup.h +++ b/tools/gator/daemon/PerfGroup.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -27,6 +27,8 @@ enum PerfGroupFlags { PERF_GROUP_TASK = 1 << 3, PERF_GROUP_SAMPLE_ID_ALL = 1 << 4, PERF_GROUP_PER_CPU = 1 << 5, + PERF_GROUP_LEADER = 1 << 6, + PERF_GROUP_CPU = 1 << 7, }; enum { @@ -40,22 +42,29 @@ public: PerfGroup(PerfBuffer *const pb); ~PerfGroup(); + bool createCpuGroup(const uint64_t currTime, Buffer *const buffer); bool add(const uint64_t currTime, Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags); // Safe to call concurrently int prepareCPU(const int cpu, Monitor *const monitor); // Not safe to call concurrently. Returns the number of events enabled - int onlineCPU(const uint64_t currTime, const int cpu, const bool start, Buffer *const buffer); + int onlineCPU(const uint64_t currTime, const int cpu, const bool enable, Buffer *const buffer); bool offlineCPU(int cpu); - bool start(); + void start(); void stop(); private: - // +1 for the group leader - struct perf_event_attr mAttrs[MAX_PERFORMANCE_COUNTERS + 1]; - bool mPerCpu[MAX_PERFORMANCE_COUNTERS + 1]; - int mKeys[MAX_PERFORMANCE_COUNTERS + 1]; - int mFds[NR_CPUS * (MAX_PERFORMANCE_COUNTERS + 1)]; + int getEffectiveType(const int type, const int flags); + int doAdd(const uint64_t currTime, Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags); + + // 2* to be conservative for sched_switch, cpu_idle, hrtimer and non-CPU groups + struct perf_event_attr mAttrs[2*MAX_PERFORMANCE_COUNTERS]; PerfBuffer *const mPb; + int mFlags[2*MAX_PERFORMANCE_COUNTERS]; + int mKeys[2*MAX_PERFORMANCE_COUNTERS]; + int mFds[NR_CPUS * (2*MAX_PERFORMANCE_COUNTERS)]; + // Offset in mAttrs, mFlags and mKeys of the group leaders for each perf type + int mLeaders[16]; + int mSchedSwitchId; // Intentionally undefined PerfGroup(const PerfGroup &); diff --git a/tools/gator/daemon/PerfSource.cpp b/tools/gator/daemon/PerfSource.cpp index 193b778..7578396 100644 --- a/tools/gator/daemon/PerfSource.cpp +++ b/tools/gator/daemon/PerfSource.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -31,24 +31,12 @@ extern Child *child; -static bool sendTracepointFormat(const uint64_t currTime, Buffer *const buffer, const char *const name, DynBuf *const printb, DynBuf *const b) { - if (!printb->printf(EVENTS_PATH "/%s/format", name)) { - logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); - return false; - } - if (!b->read(printb->getBuf())) { - logg->logMessage("%s(%s:%i): DynBuf::read failed", __FUNCTION__, __FILE__, __LINE__); - return false; - } - buffer->format(currTime, b->getLength(), b->getBuf()); - - return true; -} +static const int cpuIdleKey = getEventKey(); static void *syncFunc(void *arg) { struct timespec ts; - int64_t nextTime = gSessionData->mMonotonicStarted; + int64_t nextTime = gSessionData.mMonotonicStarted; int err; (void)arg; @@ -58,18 +46,18 @@ static void *syncFunc(void *arg) { sigset_t set; if (sigfillset(&set) != 0) { - logg->logError(__FILE__, __LINE__, "sigfillset failed"); + logg.logError("sigfillset failed"); handleException(); } if ((err = pthread_sigmask(SIG_SETMASK, &set, NULL)) != 0) { - logg->logError(__FILE__, __LINE__, "pthread_sigmask failed"); + logg.logError("pthread_sigmask failed"); handleException(); } } for (;;) { if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) { - logg->logError(__FILE__, __LINE__, "clock_gettime failed"); + logg.logError("clock_gettime failed"); handleException(); } const int64_t currTime = ts.tv_sec * NS_PER_S + ts.tv_nsec; @@ -95,7 +83,7 @@ static void *syncFunc(void *arg) static long getMaxCoreNum() { DIR *dir = opendir("/sys/devices/system/cpu"); if (dir == NULL) { - logg->logError(__FILE__, __LINE__, "Unable to determine the number of cores on the target, opendir failed"); + logg.logError("Unable to determine the number of cores on the target, opendir failed"); handleException(); } @@ -114,113 +102,106 @@ static long getMaxCoreNum() { closedir(dir); if (maxCoreNum < 1) { - logg->logError(__FILE__, __LINE__, "Unable to determine the number of cores on the target, no cpu# directories found"); + logg.logError("Unable to determine the number of cores on the target, no cpu# directories found"); handleException(); } if (maxCoreNum >= NR_CPUS) { - logg->logError(__FILE__, __LINE__, "Too many cores on the target, please increase NR_CPUS in Config.h"); + logg.logError("Too many cores on the target, please increase NR_CPUS in Config.h"); handleException(); } return maxCoreNum; } -PerfSource::PerfSource(sem_t *senderSem, sem_t *startProfile) : mSummary(0, FRAME_SUMMARY, 1024, senderSem), mBuffer(0, FRAME_PERF_ATTRS, 1024*1024, senderSem), mCountersBuf(), mCountersGroup(&mCountersBuf), mIdleGroup(&mCountersBuf), mMonitor(), mUEvent(), mSenderSem(senderSem), mStartProfile(startProfile), mInterruptFd(-1), mIsDone(false) { +PerfSource::PerfSource(sem_t *senderSem, sem_t *startProfile) : mSummary(0, FRAME_SUMMARY, 1024, senderSem), mBuffer(NULL), mCountersBuf(), mCountersGroup(&mCountersBuf), mMonitor(), mUEvent(), mSenderSem(senderSem), mStartProfile(startProfile), mInterruptFd(-1), mIsDone(false) { long l = sysconf(_SC_PAGE_SIZE); if (l < 0) { - logg->logError(__FILE__, __LINE__, "Unable to obtain the page size"); + logg.logError("Unable to obtain the page size"); handleException(); } - gSessionData->mPageSize = static_cast(l); - gSessionData->mCores = static_cast(getMaxCoreNum()); + gSessionData.mPageSize = static_cast(l); + gSessionData.mCores = static_cast(getMaxCoreNum()); } PerfSource::~PerfSource() { + delete mBuffer; } bool PerfSource::prepare() { DynBuf printb; DynBuf b1; - long long schedSwitchId; long long cpuIdleId; - const uint64_t currTime = getTime(); + // MonotonicStarted has not yet been assigned! + const uint64_t currTime = 0;//getTime() - gSessionData.mMonotonicStarted; + + mBuffer = new Buffer(0, FRAME_PERF_ATTRS, gSessionData.mTotalBufferSize*1024*1024, mSenderSem); // Reread cpuinfo since cores may have changed since startup - gSessionData->readCpuInfo(); + gSessionData.readCpuInfo(); if (0 || !mMonitor.init() || !mUEvent.init() || !mMonitor.add(mUEvent.getFd()) - || (schedSwitchId = PerfDriver::getTracepointId(SCHED_SWITCH, &printb)) < 0 - || !sendTracepointFormat(currTime, &mBuffer, SCHED_SWITCH, &printb, &b1) - || (cpuIdleId = PerfDriver::getTracepointId(CPU_IDLE, &printb)) < 0 - || !sendTracepointFormat(currTime, &mBuffer, CPU_IDLE, &printb, &b1) - // Only want RAW but not IP on sched_switch and don't want TID on SAMPLE_ID - || !mCountersGroup.add(currTime, &mBuffer, 100/**/, PERF_TYPE_TRACEPOINT, schedSwitchId, 1, PERF_SAMPLE_RAW, PERF_GROUP_MMAP | PERF_GROUP_COMM | PERF_GROUP_TASK | PERF_GROUP_SAMPLE_ID_ALL | PERF_GROUP_PER_CPU) - || !mIdleGroup.add(currTime, &mBuffer, 101/**/, PERF_TYPE_TRACEPOINT, cpuIdleId, 1, PERF_SAMPLE_RAW, PERF_GROUP_PER_CPU) + || !gSessionData.mPerf.sendTracepointFormats(currTime, mBuffer, &printb, &b1) - // Only want TID and IP but not RAW on timer - || (gSessionData->mSampleRate > 0 && !gSessionData->mIsEBS && !mCountersGroup.add(currTime, &mBuffer, 102/**/, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, 1000000000UL / gSessionData->mSampleRate, PERF_SAMPLE_TID | PERF_SAMPLE_IP, PERF_GROUP_PER_CPU)) + || !mCountersGroup.createCpuGroup(currTime, mBuffer) + || !mCountersGroup.add(currTime, mBuffer, cpuIdleKey, PERF_TYPE_TRACEPOINT, cpuIdleId, 1, PERF_SAMPLE_RAW, PERF_GROUP_LEADER | PERF_GROUP_PER_CPU) - || !gSessionData->perf.enable(currTime, &mCountersGroup, &mBuffer) + || !gSessionData.mPerf.enable(currTime, &mCountersGroup, mBuffer) || 0) { - logg->logMessage("%s(%s:%i): perf setup failed, are you running Linux 3.4 or later?", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("perf setup failed, are you running Linux 3.4 or later?"); return false; } - for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + for (int cpu = 0; cpu < gSessionData.mCores; ++cpu) { const int result = mCountersGroup.prepareCPU(cpu, &mMonitor); if ((result != PG_SUCCESS) && (result != PG_CPU_OFFLINE)) { - logg->logError(__FILE__, __LINE__, "PerfGroup::prepareCPU on mCountersGroup failed"); - handleException(); - } - } - for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { - const int result = mIdleGroup.prepareCPU(cpu, &mMonitor); - if ((result != PG_SUCCESS) && (result != PG_CPU_OFFLINE)) { - logg->logError(__FILE__, __LINE__, "PerfGroup::prepareCPU on mIdleGroup failed"); + logg.logError("PerfGroup::prepareCPU on mCountersGroup failed"); handleException(); } } int numEvents = 0; - for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { - numEvents += mCountersGroup.onlineCPU(currTime, cpu, false, &mBuffer); - } - for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { - numEvents += mIdleGroup.onlineCPU(currTime, cpu, false, &mBuffer); + for (int cpu = 0; cpu < gSessionData.mCores; ++cpu) { + numEvents += mCountersGroup.onlineCPU(currTime, cpu, false, mBuffer); } if (numEvents <= 0) { - logg->logMessage("%s(%s:%i): PerfGroup::onlineCPU failed on all cores", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("PerfGroup::onlineCPU failed on all cores"); return false; } // Send the summary right before the start so that the monotonic delta is close to the start time - if (!gSessionData->perf.summary(&mSummary)) { - logg->logError(__FILE__, __LINE__, "PerfDriver::summary failed", __FUNCTION__, __FILE__, __LINE__); - handleException(); + if (!gSessionData.mPerf.summary(&mSummary)) { + logg.logError("PerfDriver::summary failed"); + handleException(); } - // Start the timer thread to used to sync perf and monotonic raw times - pthread_t syncThread; - if (pthread_create(&syncThread, NULL, syncFunc, NULL)) { - logg->logError(__FILE__, __LINE__, "pthread_create failed", __FUNCTION__, __FILE__, __LINE__); - handleException(); - } - struct sched_param param; - param.sched_priority = sched_get_priority_max(SCHED_FIFO); - if (pthread_setschedparam(syncThread, SCHED_FIFO | SCHED_RESET_ON_FORK, ¶m) != 0) { - logg->logError(__FILE__, __LINE__, "pthread_setschedparam failed"); - handleException(); + if (!gSessionData.mPerf.getClockidSupport()) { + // Start the timer thread to used to sync perf and monotonic raw times + pthread_t syncThread; + if (pthread_create(&syncThread, NULL, syncFunc, NULL)) { + logg.logError("pthread_create failed"); + handleException(); + } + struct sched_param param; + param.sched_priority = sched_get_priority_max(SCHED_FIFO); + if (pthread_setschedparam(syncThread, SCHED_FIFO | SCHED_RESET_ON_FORK, ¶m) != 0) { + logg.logMessage("Unable to schedule sync thread as FIFO, trying OTHER"); + param.sched_priority = sched_get_priority_max(SCHED_OTHER); + if (pthread_setschedparam(syncThread, SCHED_OTHER | SCHED_RESET_ON_FORK, ¶m) != 0) { + logg.logError("pthread_setschedparam failed"); + handleException(); + } + } } - mBuffer.commit(currTime); + mBuffer->commit(currTime); return true; } @@ -231,7 +212,7 @@ struct ProcThreadArgs { bool mIsDone; }; -void *procFunc(void *arg) { +static void *procFunc(void *arg) { DynBuf printb; DynBuf b; const ProcThreadArgs *const args = (ProcThreadArgs *)arg; @@ -240,18 +221,17 @@ void *procFunc(void *arg) { // Gator runs at a high priority, reset the priority to the default if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), 0) == -1) { - logg->logError(__FILE__, __LINE__, "setpriority failed"); + logg.logError("setpriority failed"); handleException(); } if (!readProcMaps(args->mCurrTime, args->mBuffer, &printb, &b)) { - logg->logError(__FILE__, __LINE__, "readProcMaps failed"); + logg.logError("readProcMaps failed"); handleException(); } - args->mBuffer->commit(args->mCurrTime); if (!readKallsyms(args->mCurrTime, args->mBuffer, &args->mIsDone)) { - logg->logError(__FILE__, __LINE__, "readKallsyms failed"); + logg.logError("readKallsyms failed"); handleException(); } args->mBuffer->commit(args->mCurrTime); @@ -266,67 +246,69 @@ void PerfSource::run() { pthread_t procThread; ProcThreadArgs procThreadArgs; + if (pipe_cloexec(pipefd) != 0) { + logg.logError("pipe failed"); + handleException(); + } + mInterruptFd = pipefd[1]; + + if (!mMonitor.add(pipefd[0])) { + logg.logError("Monitor::add failed"); + handleException(); + } + { DynBuf printb; DynBuf b1; DynBuf b2; - const uint64_t currTime = getTime(); + const uint64_t currTime = getTime() - gSessionData.mMonotonicStarted; // Start events before reading proc to avoid race conditions - if (!mCountersGroup.start() || !mIdleGroup.start()) { - logg->logError(__FILE__, __LINE__, "PerfGroup::start failed", __FUNCTION__, __FILE__, __LINE__); - handleException(); + mCountersGroup.start(); + + mBuffer->perfCounterHeader(currTime); + for (int cpu = 0; cpu < gSessionData.mCores; ++cpu) { + gSessionData.mPerf.read(mBuffer, cpu); } + mBuffer->perfCounterFooter(currTime); - if (!readProcComms(currTime, &mBuffer, &printb, &b1, &b2)) { - logg->logError(__FILE__, __LINE__, "readProcComms failed"); + if (!readProcSysDependencies(currTime, mBuffer, &printb, &b1, &b2)) { + logg.logError("readProcSysDependencies failed"); handleException(); } - mBuffer.commit(currTime); + mBuffer->commit(currTime); // Postpone reading kallsyms as on android adb gets too backed up and data is lost - procThreadArgs.mBuffer = &mBuffer; + procThreadArgs.mBuffer = mBuffer; procThreadArgs.mCurrTime = currTime; procThreadArgs.mIsDone = false; if (pthread_create(&procThread, NULL, procFunc, &procThreadArgs)) { - logg->logError(__FILE__, __LINE__, "pthread_create failed", __FUNCTION__, __FILE__, __LINE__); + logg.logError("pthread_create failed"); handleException(); } } - if (pipe_cloexec(pipefd) != 0) { - logg->logError(__FILE__, __LINE__, "pipe failed"); - handleException(); - } - mInterruptFd = pipefd[1]; - - if (!mMonitor.add(pipefd[0])) { - logg->logError(__FILE__, __LINE__, "Monitor::add failed"); - handleException(); - } - - int timeout = -1; - if (gSessionData->mLiveRate > 0) { - timeout = gSessionData->mLiveRate/NS_PER_MS; - } - sem_post(mStartProfile); - while (gSessionData->mSessionIsActive) { + const uint64_t NO_RATE = ~0ULL; + const uint64_t rate = gSessionData.mLiveRate > 0 && gSessionData.mSampleRate > 0 ? gSessionData.mLiveRate : NO_RATE; + uint64_t nextTime = 0; + int timeout = rate != NO_RATE ? 0 : -1; + while (gSessionData.mSessionIsActive) { // +1 for uevents, +1 for pipe struct epoll_event events[NR_CPUS + 2]; int ready = mMonitor.wait(events, ARRAY_LENGTH(events), timeout); if (ready < 0) { - logg->logError(__FILE__, __LINE__, "Monitor::wait failed"); + logg.logError("Monitor::wait failed"); handleException(); } - const uint64_t currTime = getTime(); + const uint64_t currTime = getTime() - gSessionData.mMonotonicStarted; for (int i = 0; i < ready; ++i) { if (events[i].data.fd == mUEvent.getFd()) { if (!handleUEvent(currTime)) { - logg->logError(__FILE__, __LINE__, "PerfSource::handleUEvent failed"); + logg.logError("PerfSource::handleUEvent failed"); handleException(); } break; @@ -337,18 +319,24 @@ void PerfSource::run() { sem_post(mSenderSem); // In one shot mode, stop collection once all the buffers are filled - // Assume timeout == 0 in this case - if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { - logg->logMessage("%s(%s:%i): One shot", __FUNCTION__, __FILE__, __LINE__); + if (gSessionData.mOneShot && gSessionData.mSessionIsActive && ((mSummary.bytesAvailable() <= 0) || (mBuffer->bytesAvailable() <= 0) || mCountersBuf.isFull())) { + logg.logMessage("One shot (perf)"); child->endSession(); } + + if (rate != NO_RATE) { + while (currTime > nextTime) { + nextTime += rate; + } + // + NS_PER_MS - 1 to ensure always rounding up + timeout = max(0, (int)((nextTime + NS_PER_MS - 1 - getTime() + gSessionData.mMonotonicStarted)/NS_PER_MS)); + } } procThreadArgs.mIsDone = true; pthread_join(procThread, NULL); - mIdleGroup.stop(); mCountersGroup.stop(); - mBuffer.setDone(); + mBuffer->setDone(); mIsDone = true; // send a notification that data is ready @@ -362,57 +350,53 @@ void PerfSource::run() { bool PerfSource::handleUEvent(const uint64_t currTime) { UEventResult result; if (!mUEvent.read(&result)) { - logg->logMessage("%s(%s:%i): UEvent::Read failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("UEvent::Read failed"); return false; } if (strcmp(result.mSubsystem, "cpu") == 0) { if (strncmp(result.mDevPath, CPU_DEVPATH, sizeof(CPU_DEVPATH) - 1) != 0) { - logg->logMessage("%s(%s:%i): Unexpected cpu DEVPATH format", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("Unexpected cpu DEVPATH format"); return false; } char *endptr; errno = 0; int cpu = strtol(result.mDevPath + sizeof(CPU_DEVPATH) - 1, &endptr, 10); if (errno != 0 || *endptr != '\0') { - logg->logMessage("%s(%s:%i): strtol failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("strtol failed"); return false; } - if (cpu >= gSessionData->mCores) { - logg->logError(__FILE__, __LINE__, "Only %i cores are expected but core %i reports %s", gSessionData->mCores, cpu, result.mAction); + if (cpu >= gSessionData.mCores) { + logg.logError("Only %i cores are expected but core %i reports %s", gSessionData.mCores, cpu, result.mAction); handleException(); } if (strcmp(result.mAction, "online") == 0) { - mBuffer.onlineCPU(currTime, currTime - gSessionData->mMonotonicStarted, cpu); + mBuffer->onlineCPU(currTime, cpu); // Only call onlineCPU if prepareCPU succeeded - bool result = false; + bool ret = false; int err = mCountersGroup.prepareCPU(cpu, &mMonitor); if (err == PG_CPU_OFFLINE) { - result = true; + ret = true; } else if (err == PG_SUCCESS) { - if (mCountersGroup.onlineCPU(currTime, cpu, true, &mBuffer)) { - err = mIdleGroup.prepareCPU(cpu, &mMonitor); - if (err == PG_CPU_OFFLINE) { - result = true; - } else if (err == PG_SUCCESS) { - if (mIdleGroup.onlineCPU(currTime, cpu, true, &mBuffer)) { - result = true; - } - } + if (mCountersGroup.onlineCPU(currTime, cpu, true, mBuffer) > 0) { + mBuffer->perfCounterHeader(currTime); + gSessionData.mPerf.read(mBuffer, cpu); + mBuffer->perfCounterFooter(currTime); + ret = true; } } - mBuffer.commit(currTime); + mBuffer->commit(currTime); - gSessionData->readCpuInfo(); - gSessionData->perf.coreName(currTime, &mSummary, cpu); + gSessionData.readCpuInfo(); + gSessionData.mPerf.coreName(currTime, &mSummary, cpu); mSummary.commit(currTime); - return result; + return ret; } else if (strcmp(result.mAction, "offline") == 0) { - const bool result = mCountersGroup.offlineCPU(cpu) && mIdleGroup.offlineCPU(cpu); - mBuffer.offlineCPU(currTime, currTime - gSessionData->mMonotonicStarted, cpu); - return result; + const bool ret = mCountersGroup.offlineCPU(cpu); + mBuffer->offlineCPU(currTime, cpu); + return ret; } } @@ -424,26 +408,26 @@ void PerfSource::interrupt() { int8_t c = 0; // Write to the pipe to wake the monitor which will cause mSessionIsActive to be reread if (::write(mInterruptFd, &c, sizeof(c)) != sizeof(c)) { - logg->logError(__FILE__, __LINE__, "write failed"); + logg.logError("write failed"); handleException(); } } } bool PerfSource::isDone () { - return mBuffer.isDone() && mIsDone && mCountersBuf.isEmpty(); + return mBuffer->isDone() && mIsDone && mCountersBuf.isEmpty(); } void PerfSource::write (Sender *sender) { if (!mSummary.isDone()) { mSummary.write(sender); - gSessionData->mSentSummary = true; + gSessionData.mSentSummary = true; } - if (!mBuffer.isDone()) { - mBuffer.write(sender); + if (!mBuffer->isDone()) { + mBuffer->write(sender); } if (!mCountersBuf.send(sender)) { - logg->logError(__FILE__, __LINE__, "PerfBuffer::send failed"); + logg.logError("PerfBuffer::send failed"); handleException(); } } diff --git a/tools/gator/daemon/PerfSource.h b/tools/gator/daemon/PerfSource.h index ce1eafe..feec1c2 100644 --- a/tools/gator/daemon/PerfSource.h +++ b/tools/gator/daemon/PerfSource.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -36,10 +36,9 @@ private: bool handleUEvent(const uint64_t currTime); Buffer mSummary; - Buffer mBuffer; + Buffer *mBuffer; PerfBuffer mCountersBuf; PerfGroup mCountersGroup; - PerfGroup mIdleGroup; Monitor mMonitor; UEvent mUEvent; sem_t *const mSenderSem; diff --git a/tools/gator/daemon/PmuXML.cpp b/tools/gator/daemon/PmuXML.cpp new file mode 100644 index 0000000..4a4575e --- /dev/null +++ b/tools/gator/daemon/PmuXML.cpp @@ -0,0 +1,152 @@ +/** + * Copyright (C) ARM Limited 2010-2015. 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 "PmuXML.h" + +#include + +#include "mxml/mxml.h" + +#include "DriverSource.h" +#include "Logging.h" +#include "OlyUtility.h" +#include "SessionData.h" + +static const char TAG_PMUS[] = "pmus"; +static const char TAG_PMU[] = "pmu"; +static const char TAG_UNCORE_PMU[] = "uncore_pmu"; + +static const char ATTR_PMNC_NAME[] = "pmnc_name"; +static const char ATTR_CPUID[] = "cpuid"; +static const char ATTR_CORE_NAME[] = "core_name"; +static const char ATTR_DT_NAME[] = "dt_name"; +static const char ATTR_PMNC_COUNTERS[] = "pmnc_counters"; +static const char ATTR_HAS_CYCLES_COUNTER[] = "has_cycles_counter"; + +PmuXML::PmuXML() { +} + +PmuXML::~PmuXML() { +} + +void PmuXML::read(const char *const path) { + { + const char *xml; + unsigned int len; + getDefaultXml(&xml, &len); + parse(xml); + } + + if (path != NULL) { + // Parse user defined items second as they will show up first in the linked list + char *xml = readFromDisk(path); + if (xml == NULL) { + logg.logError("Unable to open additional pmus XML %s", path); + handleException(); + } + parse(xml); + free(xml); + } +} + +void PmuXML::parse(const char *const xml) { + mxml_node_t *root = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK); + + for (mxml_node_t *node = mxmlFindElement(root, root, TAG_PMU, NULL, NULL, MXML_DESCEND); + node != NULL; + node = mxmlFindElement(node, root, TAG_PMU, NULL, NULL, MXML_DESCEND)) { + const char *const pmncName = mxmlElementGetAttr(node, ATTR_PMNC_NAME); + const char *const cpuidStr = mxmlElementGetAttr(node, ATTR_CPUID); + const int cpuid = strtol(cpuidStr, NULL, 0); + const char *const coreName = mxmlElementGetAttr(node, ATTR_CORE_NAME); + const char *const dtName = mxmlElementGetAttr(node, ATTR_DT_NAME); + const char *const pmncCountersStr = mxmlElementGetAttr(node, ATTR_PMNC_COUNTERS); + const int pmncCounters = strtol(pmncCountersStr, NULL, 0); + if (pmncName == NULL || cpuid == 0 || coreName == NULL || pmncCounters == 0) { + logg.logError("A pmu from the pmu XML is missing one or more of the required attributes (%s, %s, %s and %s)", ATTR_PMNC_NAME, ATTR_CPUID, ATTR_CORE_NAME, ATTR_PMNC_COUNTERS); + handleException(); + } + new GatorCpu(strdup(coreName), strdup(pmncName), dtName == NULL ? NULL : strdup(dtName), cpuid, pmncCounters); + } + + for (mxml_node_t *node = mxmlFindElement(root, root, TAG_UNCORE_PMU, NULL, NULL, MXML_DESCEND); + node != NULL; + node = mxmlFindElement(node, root, TAG_UNCORE_PMU, NULL, NULL, MXML_DESCEND)) { + const char *const pmncName = mxmlElementGetAttr(node, ATTR_PMNC_NAME); + const char *const coreName = mxmlElementGetAttr(node, ATTR_CORE_NAME); + const char *const pmncCountersStr = mxmlElementGetAttr(node, ATTR_PMNC_COUNTERS); + const int pmncCounters = strtol(pmncCountersStr, NULL, 0); + const char *const hasCyclesCounterStr = mxmlElementGetAttr(node, ATTR_HAS_CYCLES_COUNTER); + const bool hasCyclesCounter = stringToBool(hasCyclesCounterStr, true); + if (pmncName == NULL || coreName == NULL || pmncCounters == 0) { + logg.logError("An uncore_pmu from the pmu XML is missing one or more of the required attributes (%s, %s and %s)", ATTR_PMNC_NAME, ATTR_CORE_NAME, ATTR_PMNC_COUNTERS); + handleException(); + } + new UncorePmu(strdup(coreName), strdup(pmncName), pmncCounters, hasCyclesCounter); + } + + mxmlDelete(root); +} + +void PmuXML::getDefaultXml(const char **const xml, unsigned int *const len) { +#include "pmus_xml.h" // defines and initializes char defaults_xml[] and int defaults_xml_len + *xml = (const char *)pmus_xml; + *len = pmus_xml_len; +} + +void PmuXML::writeToKernel() { + char buf[512]; + + for (GatorCpu *gatorCpu = GatorCpu::getHead(); gatorCpu != NULL; gatorCpu = gatorCpu->getNext()) { + snprintf(buf, sizeof(buf), "/dev/gator/pmu/%s", gatorCpu->getPmncName()); + if (access(buf, X_OK) == 0) { + continue; + } + DriverSource::writeDriver("/dev/gator/pmu/export", gatorCpu->getPmncName()); + snprintf(buf, sizeof(buf), "/dev/gator/pmu/%s/cpuid", gatorCpu->getPmncName()); + DriverSource::writeDriver(buf, gatorCpu->getCpuid()); + snprintf(buf, sizeof(buf), "/dev/gator/pmu/%s/core_name", gatorCpu->getPmncName()); + DriverSource::writeDriver(buf, gatorCpu->getCoreName()); + if (gatorCpu->getDtName() != NULL) { + snprintf(buf, sizeof(buf), "/dev/gator/pmu/%s/dt_name", gatorCpu->getPmncName()); + DriverSource::writeDriver(buf, gatorCpu->getDtName()); + } + snprintf(buf, sizeof(buf), "/dev/gator/pmu/%s/pmnc_counters", gatorCpu->getPmncName()); + DriverSource::writeDriver(buf, gatorCpu->getPmncCounters()); + } + + for (UncorePmu *uncorePmu = UncorePmu::getHead(); uncorePmu != NULL; uncorePmu = uncorePmu->getNext()) { + snprintf(buf, sizeof(buf), "/dev/gator/uncore_pmu/%s", uncorePmu->getPmncName()); + if (access(buf, X_OK) == 0) { + continue; + } + DriverSource::writeDriver("/dev/gator/uncore_pmu/export", uncorePmu->getPmncName()); + snprintf(buf, sizeof(buf), "/dev/gator/uncore_pmu/%s/core_name", uncorePmu->getPmncName()); + DriverSource::writeDriver(buf, uncorePmu->getCoreName()); + snprintf(buf, sizeof(buf), "/dev/gator/uncore_pmu/%s/pmnc_counters", uncorePmu->getPmncName()); + DriverSource::writeDriver(buf, uncorePmu->getPmncCounters()); + snprintf(buf, sizeof(buf), "/dev/gator/uncore_pmu/%s/has_cycles_counter", uncorePmu->getPmncName()); + DriverSource::writeDriver(buf, uncorePmu->getHasCyclesCounter()); + } + + DriverSource::writeDriver("/dev/gator/pmu_init", "1"); + + // Was any CPU detected? + bool foundCpu = false; + for (GatorCpu *gatorCpu = GatorCpu::getHead(); gatorCpu != NULL; gatorCpu = gatorCpu->getNext()) { + snprintf(buf, sizeof(buf), "/dev/gator/events/%s_cnt0", gatorCpu->getPmncName()); + if (access(buf, X_OK) == 0) { + foundCpu = true; + break; + } + } + + if (!foundCpu) { + logCpuNotFound(); + } +} diff --git a/tools/gator/daemon/PmuXML.h b/tools/gator/daemon/PmuXML.h new file mode 100644 index 0000000..a2d6222 --- /dev/null +++ b/tools/gator/daemon/PmuXML.h @@ -0,0 +1,29 @@ +/** + * Copyright (C) ARM Limited 2010-2015. 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. + */ + +#ifndef PMUXML_H +#define PMUXML_H + +class PmuXML { +public: + PmuXML(); + ~PmuXML(); + + static void read(const char *const path); + static void writeToKernel(); + +private: + static void parse(const char *const xml); + static void getDefaultXml(const char **const xml, unsigned int *const len); + + // Intentionally unimplemented + PmuXML(const PmuXML &); + PmuXML &operator=(const PmuXML &); +}; + +#endif // PMUXML_H diff --git a/tools/gator/daemon/Proc.cpp b/tools/gator/daemon/Proc.cpp index e6b26b1..a9f9a51 100644 --- a/tools/gator/daemon/Proc.cpp +++ b/tools/gator/daemon/Proc.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -31,20 +31,20 @@ struct ProcStat { static bool readProcStat(ProcStat *const ps, const char *const pathname, DynBuf *const b) { if (!b->read(pathname)) { - logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the thread exited", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("DynBuf::read failed, likely because the thread exited"); // This is not a fatal error - the thread just doesn't exist any more return true; } char *comm = strchr(b->getBuf(), '('); if (comm == NULL) { - logg->logMessage("%s(%s:%i): parsing stat failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("parsing stat failed"); return false; } ++comm; char *const str = strrchr(comm, ')'); if (str == NULL) { - logg->logMessage("%s(%s:%i): parsing stat failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("parsing stat failed"); return false; } *str = '\0'; @@ -53,7 +53,7 @@ static bool readProcStat(ProcStat *const ps, const char *const pathname, DynBuf const int count = sscanf(str + 2, " %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %ld", &ps->numThreads); if (count != 1) { - logg->logMessage("%s(%s:%i): sscanf failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("sscanf failed"); return false; } @@ -65,41 +65,36 @@ static const char APP_PROCESS[] = "app_process"; static const char *readProcExe(DynBuf *const printb, const int pid, const int tid, DynBuf *const b) { if (tid == -1 ? !printb->printf("/proc/%i/exe", pid) : !printb->printf("/proc/%i/task/%i/exe", pid, tid)) { - logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("DynBuf::printf failed"); return NULL; } const int err = b->readlink(printb->getBuf()); const char *image; if (err == 0) { - image = strrchr(b->getBuf(), '/'); - if (image == NULL) { - image = b->getBuf(); - } else { - ++image; - } + image = b->getBuf(); } else if (err == -ENOENT) { // readlink /proc/[pid]/exe returns ENOENT for kernel threads image = "\0"; } else { - logg->logMessage("%s(%s:%i): DynBuf::readlink failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("DynBuf::readlink failed"); return NULL; } // Android apps are run by app_process but the cmdline is changed to reference the actual app name // On 64-bit android app_process can be app_process32 or app_process64 - if (strncmp(image, APP_PROCESS, sizeof(APP_PROCESS) - 1) != 0) { + if (strstr(image, APP_PROCESS) == NULL) { return image; } if (tid == -1 ? !printb->printf("/proc/%i/cmdline", pid) : !printb->printf("/proc/%i/task/%i/cmdline", pid, tid)) { - logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("DynBuf::printf failed"); return NULL; } if (!b->read(printb->getBuf())) { - logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the thread exited", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("DynBuf::read failed, likely because the thread exited"); return NULL; } @@ -110,12 +105,12 @@ static bool readProcTask(const uint64_t currTime, Buffer *const buffer, const in bool result = false; if (!b1->printf("/proc/%i/task", pid)) { - logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("DynBuf::printf failed"); return result; } DIR *task = opendir(b1->getBuf()); if (task == NULL) { - logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("opendir failed"); // This is not a fatal error - the thread just doesn't exist any more return true; } @@ -130,22 +125,22 @@ static bool readProcTask(const uint64_t currTime, Buffer *const buffer, const in } if (!printb->printf("/proc/%i/task/%i/stat", pid, tid)) { - logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("DynBuf::printf failed"); goto fail; } ProcStat ps; if (!readProcStat(&ps, printb->getBuf(), b1)) { - logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("readProcStat failed"); goto fail; } const char *const image = readProcExe(printb, pid, tid, b2); if (image == NULL) { - logg->logMessage("%s(%s:%i): readImage failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("readImage failed"); goto fail; } - buffer->comm(currTime, pid, tid, image, ps.comm); + buffer->marshalComm(currTime, pid, tid, image, ps.comm); } result = true; @@ -156,12 +151,12 @@ static bool readProcTask(const uint64_t currTime, Buffer *const buffer, const in return result; } -bool readProcComms(const uint64_t currTime, Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2) { +bool readProcSysDependencies(const uint64_t currTime, Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2) { bool result = false; DIR *proc = opendir("/proc"); if (proc == NULL) { - logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("opendir failed"); return result; } @@ -175,31 +170,38 @@ bool readProcComms(const uint64_t currTime, Buffer *const buffer, DynBuf *const } if (!printb->printf("/proc/%i/stat", pid)) { - logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("DynBuf::printf failed"); goto fail; } ProcStat ps; if (!readProcStat(&ps, printb->getBuf(), b1)) { - logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("readProcStat failed"); goto fail; } if (ps.numThreads <= 1) { const char *const image = readProcExe(printb, pid, -1, b1); if (image == NULL) { - logg->logMessage("%s(%s:%i): readImage failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("readImage failed"); goto fail; } - buffer->comm(currTime, pid, pid, image, ps.comm); + buffer->marshalComm(currTime, pid, pid, image, ps.comm); } else { if (!readProcTask(currTime, buffer, pid, printb, b1, b2)) { - logg->logMessage("%s(%s:%i): readProcTask failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("readProcTask failed"); goto fail; } } } + if (gSessionData.mFtraceRaw) { + if (!gSessionData.mFtraceDriver.readTracepointFormats(currTime, buffer, printb, b1)) { + logg.logMessage("FtraceDriver::readTracepointFormats failed"); + goto fail; + } + } + result = true; fail: @@ -213,7 +215,7 @@ bool readProcMaps(const uint64_t currTime, Buffer *const buffer, DynBuf *const p DIR *proc = opendir("/proc"); if (proc == NULL) { - logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("opendir failed"); return result; } @@ -227,16 +229,16 @@ bool readProcMaps(const uint64_t currTime, Buffer *const buffer, DynBuf *const p } if (!printb->printf("/proc/%i/maps", pid)) { - logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("DynBuf::printf failed"); goto fail; } if (!b->read(printb->getBuf())) { - logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the process exited", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("DynBuf::read failed, likely because the process exited"); // This is not a fatal error - the process just doesn't exist any more continue; } - buffer->maps(currTime, pid, pid, b->getBuf()); + buffer->marshalMaps(currTime, pid, pid, b->getBuf()); } result = true; @@ -251,16 +253,16 @@ bool readKallsyms(const uint64_t currTime, Buffer *const buffer, const bool *con int fd = ::open("/proc/kallsyms", O_RDONLY | O_CLOEXEC); if (fd < 0) { - logg->logMessage("%s(%s:%i): open failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("open failed"); return true; }; char buf[1<<12]; ssize_t pos = 0; - while (gSessionData->mSessionIsActive && !ACCESS_ONCE(*isDone)) { + while (gSessionData.mSessionIsActive && !ACCESS_ONCE(*isDone)) { // Assert there is still space in the buffer if (sizeof(buf) - pos - 1 == 0) { - logg->logError(__FILE__, __LINE__, "no space left in buffer"); + logg.logError("no space left in buffer"); handleException(); } @@ -268,13 +270,13 @@ bool readKallsyms(const uint64_t currTime, Buffer *const buffer, const bool *con // -1 to reserve space for \0 const ssize_t bytes = ::read(fd, buf + pos, sizeof(buf) - pos - 1); if (bytes < 0) { - logg->logError(__FILE__, __LINE__, "read failed", __FUNCTION__, __FILE__, __LINE__); + logg.logError("read failed"); handleException(); } if (bytes == 0) { // Assert the buffer is empty if (pos != 0) { - logg->logError(__FILE__, __LINE__, "buffer not empty on eof"); + logg.logError("buffer not empty on eof"); handleException(); } break; @@ -288,13 +290,13 @@ bool readKallsyms(const uint64_t currTime, Buffer *const buffer, const bool *con if (buf[newline] == '\n') { const char was = buf[newline + 1]; buf[newline + 1] = '\0'; - buffer->kallsyms(currTime, buf); + buffer->marshalKallsyms(currTime, buf); // Sleep 3 ms to avoid sending out too much data too quickly usleep(3000); buf[0] = was; // Assert the memory regions do not overlap if (pos - newline >= newline + 1) { - logg->logError(__FILE__, __LINE__, "memcpy src and dst overlap"); + logg.logError("memcpy src and dst overlap"); handleException(); } if (pos - newline - 2 > 0) { @@ -310,3 +312,17 @@ bool readKallsyms(const uint64_t currTime, Buffer *const buffer, const bool *con return true; } + +bool readTracepointFormat(const uint64_t currTime, Buffer *const buffer, const char *const name, DynBuf *const printb, DynBuf *const b) { + if (!printb->printf(EVENTS_PATH "/%s/format", name)) { + logg.logMessage("DynBuf::printf failed"); + return false; + } + if (!b->read(printb->getBuf())) { + logg.logMessage("DynBuf::read failed"); + return false; + } + buffer->marshalFormat(currTime, b->getLength(), b->getBuf()); + + return true; +} diff --git a/tools/gator/daemon/Proc.h b/tools/gator/daemon/Proc.h index 2a1a7cb..e1c2968 100644 --- a/tools/gator/daemon/Proc.h +++ b/tools/gator/daemon/Proc.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -14,8 +14,9 @@ class Buffer; class DynBuf; -bool readProcComms(const uint64_t currTime, Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2); +bool readProcSysDependencies(const uint64_t currTime, Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2); bool readProcMaps(const uint64_t currTime, Buffer *const buffer, DynBuf *const printb, DynBuf *const b); bool readKallsyms(const uint64_t currTime, Buffer *const buffer, const bool *const isDone); +bool readTracepointFormat(const uint64_t currTime, Buffer *const buffer, const char *const name, DynBuf *const printb, DynBuf *const b); #endif // PROC_H diff --git a/tools/gator/daemon/Sender.cpp b/tools/gator/daemon/Sender.cpp index 8a54a66..951eb74 100644 --- a/tools/gator/daemon/Sender.cpp +++ b/tools/gator/daemon/Sender.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -30,7 +30,7 @@ Sender::Sender(OlySocket* socket) { // Streamline will send data prior to the magic sequence for legacy support, which should be ignored for v4+ while (strcmp("STREAMLINE", streamline) != 0) { if (mDataSocket->receiveString(streamline, sizeof(streamline)) == -1) { - logg->logError(__FILE__, __LINE__, "Socket disconnected"); + logg.logError("Socket disconnected"); handleException(); } } @@ -40,11 +40,19 @@ Sender::Sender(OlySocket* socket) { snprintf(magic, 32, "GATOR %i\n", PROTOCOL_VERSION); mDataSocket->send(magic, strlen(magic)); - gSessionData->mWaitingOnCommand = true; - logg->logMessage("Completed magic sequence"); + gSessionData.mWaitingOnCommand = true; + logg.logMessage("Completed magic sequence"); } - pthread_mutex_init(&mSendMutex, NULL); + pthread_mutexattr_t attr; + if (pthread_mutexattr_init(&attr) != 0 || + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK) != 0 || + pthread_mutex_init(&mSendMutex, &attr) != 0 || + pthread_mutexattr_destroy(&attr) != 0 || + false) { + logg.logError("Unable to setup mutex"); + handleException(); + } } Sender::~Sender() { @@ -67,18 +75,24 @@ void Sender::createDataFile(char* apcDir) { sprintf(mDataFileName, "%s/0000000000", apcDir); mDataFile = fopen_cloexec(mDataFileName, "wb"); if (!mDataFile) { - logg->logError(__FILE__, __LINE__, "Failed to open binary file: %s", mDataFileName); + logg.logError("Failed to open binary file: %s", mDataFileName); handleException(); } } -void Sender::writeData(const char* data, int length, int type) { +void Sender::writeData(const char* data, int length, int type, bool ignoreLockErrors) { if (length < 0 || (data == NULL && length > 0)) { return; } // Multiple threads call writeData() - pthread_mutex_lock(&mSendMutex); + if (pthread_mutex_lock(&mSendMutex) != 0) { + if (ignoreLockErrors) { + return; + } + logg.logError("pthread_mutex_lock failed"); + handleException(); + } // Send data over the socket connection if (mDataSocket) { @@ -87,7 +101,7 @@ void Sender::writeData(const char* data, int length, int type) { alarm(alarmDuration); // Send data over the socket, sending the type and size first - logg->logMessage("Sending data with length %d", length); + logg.logMessage("Sending data with length %d", length); if (type != RESPONSE_APC_DATA) { // type and length already added by the Collector for apc data unsigned char header[5]; @@ -100,7 +114,7 @@ void Sender::writeData(const char* data, int length, int type) { const int chunkSize = 100*1000 * alarmDuration / 8; int pos = 0; while (true) { - mDataSocket->send((const char*)data + pos, min(length - pos, chunkSize)); + mDataSocket->send(data + pos, min(length - pos, chunkSize)); pos += chunkSize; if (pos >= length) { break; @@ -108,7 +122,7 @@ void Sender::writeData(const char* data, int length, int type) { // Reset the alarm alarm(alarmDuration); - logg->logMessage("Resetting the alarm"); + logg.logMessage("Resetting the alarm"); } // Stop alarm @@ -117,13 +131,16 @@ void Sender::writeData(const char* data, int length, int type) { // Write data to disk as long as it is not meta data if (mDataFile && type == RESPONSE_APC_DATA) { - logg->logMessage("Writing data with length %d", length); + logg.logMessage("Writing data with length %d", length); // Send data to the data file if (fwrite(data, 1, length, mDataFile) != (unsigned int)length) { - logg->logError(__FILE__, __LINE__, "Failed writing binary file %s", mDataFileName); + logg.logError("Failed writing binary file %s", mDataFileName); handleException(); } } - pthread_mutex_unlock(&mSendMutex); + if (pthread_mutex_unlock(&mSendMutex) != 0) { + logg.logError("pthread_mutex_unlock failed"); + handleException(); + } } diff --git a/tools/gator/daemon/Sender.h b/tools/gator/daemon/Sender.h index 5aa9117..010a222 100644 --- a/tools/gator/daemon/Sender.h +++ b/tools/gator/daemon/Sender.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -26,8 +26,9 @@ class Sender { public: Sender(OlySocket* socket); ~Sender(); - void writeData(const char* data, int length, int type); + void writeData(const char* data, int length, int type, bool ignoreLockErrors = false); void createDataFile(char* apcDir); + private: OlySocket* mDataSocket; FILE* mDataFile; diff --git a/tools/gator/daemon/SessionData.cpp b/tools/gator/daemon/SessionData.cpp index 0e65d78..8f61b09 100644 --- a/tools/gator/daemon/SessionData.cpp +++ b/tools/gator/daemon/SessionData.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -11,9 +11,9 @@ #include #include #include +#include #include -#include "CPUFreqDriver.h" #include "DiskIODriver.h" #include "FSDriver.h" #include "HwmonDriver.h" @@ -24,36 +24,101 @@ #define CORE_NAME_UNKNOWN "unknown" -SessionData* gSessionData = NULL; +const char MALI_GRAPHICS[] = "\0mali_thirdparty_server"; +const size_t MALI_GRAPHICS_SIZE = sizeof(MALI_GRAPHICS); + +SessionData gSessionData; + +GatorCpu *GatorCpu::mHead; + +GatorCpu::GatorCpu(const char *const coreName, const char *const pmncName, const char *const dtName, const int cpuid, const int pmncCounters) : mNext(mHead), mCoreName(coreName), mPmncName(pmncName), mDtName(dtName), mCpuid(cpuid), mPmncCounters(pmncCounters) { + mHead = this; +} + +static const char OLD_PMU_PREFIX[] = "ARMv7 Cortex-"; +static const char NEW_PMU_PREFIX[] = "ARMv7_Cortex_"; + +GatorCpu *GatorCpu::find(const char *const name) { + GatorCpu *gatorCpu; + + for (gatorCpu = mHead; gatorCpu != NULL; gatorCpu = gatorCpu->mNext) { + if (strcasecmp(gatorCpu->mPmncName, name) == 0 || + // Do these names match but have the old vs new prefix? + ((strncasecmp(name, OLD_PMU_PREFIX, sizeof(OLD_PMU_PREFIX) - 1) == 0 && + strncasecmp(gatorCpu->mPmncName, NEW_PMU_PREFIX, sizeof(NEW_PMU_PREFIX) - 1) == 0 && + strcasecmp(name + sizeof(OLD_PMU_PREFIX) - 1, gatorCpu->mPmncName + sizeof(NEW_PMU_PREFIX) - 1) == 0))) { + break; + } + } + + return gatorCpu; +} + +GatorCpu *GatorCpu::find(const int cpuid) { + GatorCpu *gatorCpu; + + for (gatorCpu = mHead; gatorCpu != NULL; gatorCpu = gatorCpu->mNext) { + if (gatorCpu->mCpuid == cpuid) { + break; + } + } + + return gatorCpu; +} + +UncorePmu *UncorePmu::mHead; + +UncorePmu::UncorePmu(const char *const coreName, const char *const pmncName, const int pmncCounters, const bool hasCyclesCounter) : mNext(mHead), mCoreName(coreName), mPmncName(pmncName), mPmncCounters(pmncCounters), mHasCyclesCounter(hasCyclesCounter) { + mHead = this; +} + +UncorePmu *UncorePmu::find(const char *const name) { + UncorePmu *gatorCpu; + + for (gatorCpu = mHead; gatorCpu != NULL; gatorCpu = gatorCpu->mNext) { + if (strcasecmp(name, gatorCpu->mPmncName) == 0) { + break; + } + } + + return gatorCpu; +} + +SharedData::SharedData() : mMaliUtgardCountersSize(0) { + memset(mCpuIds, -1, sizeof(mCpuIds)); +} SessionData::SessionData() { - usDrivers[0] = new HwmonDriver(); - usDrivers[1] = new FSDriver(); - usDrivers[2] = new MemInfoDriver(); - usDrivers[3] = new NetDriver(); - usDrivers[4] = new CPUFreqDriver(); - usDrivers[5] = new DiskIODriver(); - initialize(); } SessionData::~SessionData() { } +// Needed to use placement new +inline void *operator new(size_t, void *ptr) { return ptr; } + void SessionData::initialize() { + mUsDrivers[0] = new HwmonDriver(); + mUsDrivers[1] = new FSDriver(); + mUsDrivers[2] = new MemInfoDriver(); + mUsDrivers[3] = new NetDriver(); + mUsDrivers[4] = new DiskIODriver(); + + mSharedData = (SharedData *)mmap(NULL, sizeof(*mSharedData), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (mSharedData == MAP_FAILED) { + logg.logError("Unable to mmap shared memory for cpuids"); + handleException(); + } + // Use placement new to construct but not allocate the object + new ((char *)mSharedData) SharedData(); + mWaitingOnCommand = false; mSessionIsActive = false; mLocalCapture = false; mOneShot = false; mSentSummary = false; mAllowCommands = false; - const size_t cpuIdSize = sizeof(int)*NR_CPUS; - // Share mCpuIds across all instances of gatord - mCpuIds = (int *)mmap(NULL, cpuIdSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - if (mCpuIds == MAP_FAILED) { - logg->logError(__FILE__, __LINE__, "Unable to mmap shared memory for cpuids"); - handleException(); - } - memset(mCpuIds, -1, cpuIdSize); + mFtraceRaw = false; strcpy(mCoreName, CORE_NAME_UNKNOWN); readModel(); readCpuInfo(); @@ -61,6 +126,7 @@ void SessionData::initialize() { mConfigurationXMLPath = NULL; mSessionXMLPath = NULL; mEventsXMLPath = NULL; + mEventsXMLAppend = NULL; mTargetPath = NULL; mAPCDir = NULL; mCaptureWorkingDir = NULL; @@ -75,6 +141,7 @@ void SessionData::initialize() { // sysconf(_SC_NPROCESSORS_CONF) is unreliable on 2.6 Android, get the value from the kernel module mCores = 1; mPageSize = 0; + mAnnotateStart = -1; } void SessionData::parseSessionXML(char* xmlString) { @@ -83,15 +150,15 @@ void SessionData::parseSessionXML(char* xmlString) { // Set session data values - use prime numbers just below the desired value to reduce the chance of events firing at the same time if (strcmp(session.parameters.sample_rate, "high") == 0) { - mSampleRate = 9973; // 10000 + mSampleRate = 10007; // 10000 } else if (strcmp(session.parameters.sample_rate, "normal") == 0) { - mSampleRate = 997; // 1000 + mSampleRate = 1009; // 1000 } else if (strcmp(session.parameters.sample_rate, "low") == 0) { - mSampleRate = 97; // 100 + mSampleRate = 101; // 100 } else if (strcmp(session.parameters.sample_rate, "none") == 0) { mSampleRate = 0; } else { - logg->logError(__FILE__, __LINE__, "Invalid sample rate (%s) in session xml.", session.parameters.sample_rate); + logg.logError("Invalid sample rate (%s) in session xml.", session.parameters.sample_rate); handleException(); } mBacktraceDepth = session.parameters.call_stack_unwinding == true ? 128 : 0; @@ -108,25 +175,25 @@ void SessionData::parseSessionXML(char* xmlString) { } else if (strcmp(session.parameters.buffer_mode, "large") == 0) { mTotalBufferSize = 16; } else { - logg->logError(__FILE__, __LINE__, "Invalid value for buffer mode in session xml."); + logg.logError("Invalid value for buffer mode in session xml."); handleException(); } // Convert milli- to nanoseconds mLiveRate = session.parameters.live_rate * (int64_t)1000000; if (mLiveRate > 0 && mLocalCapture) { - logg->logMessage("Local capture is not compatable with live, disabling live"); + logg.logMessage("Local capture is not compatable with live, disabling live"); mLiveRate = 0; } if (!mAllowCommands && (mCaptureCommand != NULL)) { - logg->logError(__FILE__, __LINE__, "Running a command during a capture is not currently allowed. Please restart gatord with the -a flag."); + logg.logError("Running a command during a capture is not currently allowed. Please restart gatord with the -a flag."); handleException(); } } void SessionData::readModel() { - FILE *fh = fopen("/proc/device-tree/model", "rb"); + FILE *fh = fopen_cloexec("/proc/device-tree/model", "rb"); if (fh == NULL) { return; } @@ -139,61 +206,87 @@ void SessionData::readModel() { fclose(fh); } +static void setImplementer(int &cpuId, const int implementer) { + if (cpuId == -1) { + cpuId = 0; + } + cpuId |= implementer << 12; +} + +static void setPart(int &cpuId, const int part) { + if (cpuId == -1) { + cpuId = 0; + } + cpuId |= part; +} + void SessionData::readCpuInfo() { char temp[256]; // arbitrarily large amount mMaxCpuId = -1; - FILE *f = fopen("/proc/cpuinfo", "r"); + FILE *f = fopen_cloexec("/proc/cpuinfo", "r"); if (f == NULL) { - logg->logMessage("Error opening /proc/cpuinfo\n" + logg.logMessage("Error opening /proc/cpuinfo\n" "The core name in the captured xml file will be 'unknown'."); return; } - bool foundCoreName = false; + bool foundCoreName = (strcmp(mCoreName, CORE_NAME_UNKNOWN) != 0); int processor = -1; while (fgets(temp, sizeof(temp), f)) { const size_t len = strlen(temp); + if (len > 0) { + // Replace the line feed with a null + temp[len - 1] = '\0'; + } + + logg.logMessage("cpuinfo: %s", temp); + if (len == 1) { // New section, clear the processor. Streamline will not know the cpus if the pre Linux 3.8 format of cpuinfo is encountered but also that no incorrect information will be transmitted. processor = -1; continue; } - if (len > 0) { - // Replace the line feed with a null - temp[len - 1] = '\0'; - } - - const bool foundHardware = strstr(temp, "Hardware") != 0; + const bool foundHardware = !foundCoreName && strstr(temp, "Hardware") != 0; + const bool foundCPUImplementer = strstr(temp, "CPU implementer") != 0; const bool foundCPUPart = strstr(temp, "CPU part") != 0; const bool foundProcessor = strstr(temp, "processor") != 0; - if (foundHardware || foundCPUPart || foundProcessor) { + if (foundHardware || foundCPUImplementer || foundCPUPart || foundProcessor) { char* position = strchr(temp, ':'); if (position == NULL || (unsigned int)(position - temp) + 2 >= strlen(temp)) { - logg->logMessage("Unknown format of /proc/cpuinfo\n" + logg.logMessage("Unknown format of /proc/cpuinfo\n" "The core name in the captured xml file will be 'unknown'."); return; } position += 2; - if (foundHardware && (strcmp(mCoreName, CORE_NAME_UNKNOWN) == 0)) { + if (foundHardware) { strncpy(mCoreName, position, sizeof(mCoreName)); mCoreName[sizeof(mCoreName) - 1] = 0; // strncpy does not guarantee a null-terminated string foundCoreName = true; } + if (foundCPUImplementer) { + const int implementer = strtol(position, NULL, 0); + if (processor >= NR_CPUS) { + logg.logMessage("Too many processors, please increase NR_CPUS"); + } else if (processor >= 0) { + setImplementer(mSharedData->mCpuIds[processor], implementer); + } else { + setImplementer(mMaxCpuId, implementer); + } + } + if (foundCPUPart) { const int cpuId = strtol(position, NULL, 0); - // If this does not have the full topology in /proc/cpuinfo, mCpuIds[0] may not have the 1 CPU part emitted - this guarantees it's in mMaxCpuId - if (cpuId > mMaxCpuId) { - mMaxCpuId = cpuId; - } if (processor >= NR_CPUS) { - logg->logMessage("Too many processors, please increase NR_CPUS"); + logg.logMessage("Too many processors, please increase NR_CPUS"); } else if (processor >= 0) { - mCpuIds[processor] = cpuId; + setPart(mSharedData->mCpuIds[processor], cpuId); + } else { + setPart(mMaxCpuId, cpuId); } } @@ -203,8 +296,15 @@ void SessionData::readCpuInfo() { } } + // If this does not have the full topology in /proc/cpuinfo, mCpuIds[0] may not have the 1 CPU part emitted - this guarantees it's in mMaxCpuId + for (int i = 0; i < NR_CPUS; ++i) { + if (mSharedData->mCpuIds[i] > mMaxCpuId) { + mMaxCpuId = mSharedData->mCpuIds[i]; + } + } + if (!foundCoreName) { - logg->logMessage("Could not determine core name from /proc/cpuinfo\n" + logg.logMessage("Could not determine core name from /proc/cpuinfo\n" "The core name in the captured xml file will be 'unknown'."); } fclose(f); @@ -213,7 +313,7 @@ void SessionData::readCpuInfo() { uint64_t getTime() { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) { - logg->logError(__FILE__, __LINE__, "Failed to get uptime"); + logg.logError("Failed to get uptime"); handleException(); } return (NS_PER_S*ts.tv_sec + ts.tv_nsec); @@ -259,3 +359,75 @@ FILE *fopen_cloexec(const char *path, const char *mode) { } return fh; } + +bool setNonblock(const int fd) { + int flags; + + flags = fcntl(fd, F_GETFL); + if (flags < 0) { + logg.logMessage("fcntl getfl failed"); + return false; + } + + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) { + logg.logMessage("fcntl setfl failed"); + return false; + } + + return true; +} + +bool writeAll(const int fd, const void *const buf, const size_t pos) { + size_t written = 0; + while (written < pos) { + ssize_t bytes = write(fd, (const uint8_t *)buf + written, pos - written); + if (bytes <= 0) { + logg.logMessage("write failed"); + return false; + } + written += bytes; + } + + return true; +} + +bool readAll(const int fd, void *const buf, const size_t count) { + size_t pos = 0; + while (pos < count) { + ssize_t bytes = read(fd, (uint8_t *)buf + pos, count - pos); + if (bytes <= 0) { + logg.logMessage("read failed"); + return false; + } + pos += bytes; + } + + return true; +} + +bool getLinuxVersion(int version[3]) { + // Check the kernel version + struct utsname utsname; + if (uname(&utsname) != 0) { + logg.logMessage("uname failed"); + return false; + } + + version[0] = 0; + version[1] = 0; + version[2] = 0; + + int part = 0; + char *ch = utsname.release; + while (*ch >= '0' && *ch <= '9' && part < 3) { + version[part] = 10*version[part] + *ch - '0'; + + ++ch; + if (*ch == '.') { + ++part; + ++ch; + } + } + + return true; +} diff --git a/tools/gator/daemon/SessionData.h b/tools/gator/daemon/SessionData.h index ed282af..38e8163 100644 --- a/tools/gator/daemon/SessionData.h +++ b/tools/gator/daemon/SessionData.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -11,50 +11,167 @@ #include -#include "AnnotateListener.h" +#include "AtraceDriver.h" +#include "CCNDriver.h" #include "Config.h" #include "Counter.h" +#include "ExternalDriver.h" #include "FtraceDriver.h" #include "KMod.h" #include "MaliVideoDriver.h" +#include "MidgardDriver.h" #include "PerfDriver.h" +#include "TtraceDriver.h" -#define PROTOCOL_VERSION 20 +#define PROTOCOL_VERSION 231 // Differentiates development versions (timestamp) from release versions -#define PROTOCOL_DEV 1000 +#define PROTOCOL_DEV 10000000 #define NS_PER_S 1000000000LL #define NS_PER_MS 1000000LL #define NS_PER_US 1000LL +extern const char MALI_GRAPHICS[]; +extern const size_t MALI_GRAPHICS_SIZE; + struct ImageLinkList { char* path; struct ImageLinkList *next; }; +class GatorCpu { +public: + GatorCpu(const char *const coreName, const char *const pmncName, const char *const dtName, const int cpuid, const int pmncCounters); + + static GatorCpu *getHead() { + return mHead; + } + + GatorCpu *getNext() const { + return mNext; + } + + const char *getCoreName() const { + return mCoreName; + } + + const char *getPmncName() const { + return mPmncName; + } + + const char *getDtName() const { + return mDtName; + } + + int getCpuid() const { + return mCpuid; + } + + int getPmncCounters() const { + return mPmncCounters; + } + + static GatorCpu *find(const char *const name); + + static GatorCpu *find(const int cpuid); + +private: + static GatorCpu *mHead; + GatorCpu *const mNext; + const char *const mCoreName; + const char *const mPmncName; + const char *const mDtName; + const int mCpuid; + const int mPmncCounters; +}; + +class UncorePmu { +public: + UncorePmu(const char *const coreName, const char *const pmncName, const int pmncCounters, const bool hasCyclesCounter); + + static UncorePmu *getHead() { + return mHead; + } + + UncorePmu *getNext() const { + return mNext; + } + + const char *getCoreName() const { + return mCoreName; + } + + const char *getPmncName() const { + return mPmncName; + } + + int getPmncCounters() const { + return mPmncCounters; + } + + bool getHasCyclesCounter() const { + return mHasCyclesCounter; + } + + static UncorePmu *find(const char *const name); + +private: + static UncorePmu *mHead; + UncorePmu *const mNext; + const char *const mCoreName; + const char *const mPmncName; + const int mPmncCounters; + const bool mHasCyclesCounter; +}; + +class SharedData { +public: + SharedData(); + + int mCpuIds[NR_CPUS]; + size_t mMaliUtgardCountersSize; + char mMaliUtgardCounters[1<<12]; + size_t mMaliMidgardCountersSize; + char mMaliMidgardCounters[1<<13]; + +private: + // Intentionally unimplemented + SharedData(const SharedData &); + SharedData &operator=(const SharedData &); +}; + class SessionData { public: static const size_t MAX_STRING_LEN = 80; SessionData(); ~SessionData(); + void initialize(); void parseSessionXML(char* xmlString); void readModel(); void readCpuInfo(); - PolledDriver *usDrivers[6]; - KMod kmod; - PerfDriver perf; - MaliVideoDriver maliVideo; - FtraceDriver ftraceDriver; - AnnotateListener annotateListener; + SharedData *mSharedData; + + PolledDriver *mUsDrivers[5]; + KMod mKmod; + PerfDriver mPerf; + MaliVideoDriver mMaliVideo; + MidgardDriver mMidgard; + // Intentionally above FtraceDriver as drivers are initialized in reverse order AtraceDriver and TtraceDriver references FtraceDriver + AtraceDriver mAtraceDriver; + TtraceDriver mTtraceDriver; + FtraceDriver mFtraceDriver; + ExternalDriver mExternalDriver; + CCNDriver mCcnDriver; char mCoreName[MAX_STRING_LEN]; struct ImageLinkList *mImages; char *mConfigurationXMLPath; char *mSessionXMLPath; char *mEventsXMLPath; + char *mEventsXMLAppend; char *mTargetPath; char *mAPCDir; char *mCaptureWorkingDir; @@ -69,6 +186,7 @@ public: bool mIsEBS; bool mSentSummary; bool mAllowCommands; + bool mFtraceRaw; int64_t mMonotonicStarted; int mBacktraceDepth; @@ -79,11 +197,11 @@ public: int mDuration; int mCores; int mPageSize; - int *mCpuIds; int mMaxCpuId; + int mAnnotateStart; // PMU Counters - int mCounterOverflow; + char *mCountersError; Counter mCounters[MAX_PERFORMANCE_COUNTERS]; private: @@ -92,11 +210,21 @@ private: SessionData &operator=(const SessionData &); }; -extern SessionData* gSessionData; +extern SessionData gSessionData; +extern const char *const gSrcMd5; uint64_t getTime(); int getEventKey(); int pipe_cloexec(int pipefd[2]); FILE *fopen_cloexec(const char *path, const char *mode); +bool setNonblock(const int fd); +bool writeAll(const int fd, const void *const buf, const size_t pos); +bool readAll(const int fd, void *const buf, const size_t count); +void logCpuNotFound(); + +// From include/generated/uapi/linux/version.h +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +bool getLinuxVersion(int version[3]); #endif // SESSION_DATA_H diff --git a/tools/gator/daemon/SessionXML.cpp b/tools/gator/daemon/SessionXML.cpp index dea4c8f..742f0c4 100644 --- a/tools/gator/daemon/SessionXML.cpp +++ b/tools/gator/daemon/SessionXML.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -16,19 +16,20 @@ #include "OlyUtility.h" #include "SessionData.h" -static const char *TAG_SESSION = "session"; -static const char *TAG_IMAGE = "image"; - -static const char *ATTR_VERSION = "version"; -static const char *ATTR_CALL_STACK_UNWINDING = "call_stack_unwinding"; -static const char *ATTR_BUFFER_MODE = "buffer_mode"; -static const char *ATTR_SAMPLE_RATE = "sample_rate"; -static const char *ATTR_DURATION = "duration"; -static const char *ATTR_PATH = "path"; -static const char *ATTR_LIVE_RATE = "live_rate"; -static const char *ATTR_CAPTURE_WORKING_DIR = "capture_working_dir"; -static const char *ATTR_CAPTURE_COMMAND = "capture_command"; -static const char *ATTR_CAPTURE_USER = "capture_user"; +static const char TAG_SESSION[] = "session"; +static const char TAG_IMAGE[] = "image"; + +static const char ATTR_VERSION[] = "version"; +static const char ATTR_CALL_STACK_UNWINDING[] = "call_stack_unwinding"; +static const char ATTR_BUFFER_MODE[] = "buffer_mode"; +static const char ATTR_SAMPLE_RATE[] = "sample_rate"; +static const char ATTR_DURATION[] = "duration"; +static const char USE_EFFICIENT_FTRACE[] = "use_efficient_ftrace"; +static const char ATTR_PATH[] = "path"; +static const char ATTR_LIVE_RATE[] = "live_rate"; +static const char ATTR_CAPTURE_WORKING_DIR[] = "capture_working_dir"; +static const char ATTR_CAPTURE_COMMAND[] = "capture_command"; +static const char ATTR_CAPTURE_USER[] = "capture_user"; SessionXML::SessionXML(const char *str) { parameters.buffer_mode[0] = 0; @@ -36,7 +37,7 @@ SessionXML::SessionXML(const char *str) { parameters.call_stack_unwinding = false; parameters.live_rate = 0; mSessionXML = str; - logg->logMessage(mSessionXML); + logg.logMessage("%s", mSessionXML); } SessionXML::~SessionXML() { @@ -55,7 +56,7 @@ void SessionXML::parse() { return; } - logg->logError(__FILE__, __LINE__, "No session tag found in the session.xml file"); + logg.logError("No session tag found in the session.xml file"); handleException(); } @@ -63,7 +64,7 @@ void SessionXML::sessionTag(mxml_node_t *tree, mxml_node_t *node) { int version = 0; if (mxmlElementGetAttr(node, ATTR_VERSION)) version = strtol(mxmlElementGetAttr(node, ATTR_VERSION), NULL, 10); if (version != 1) { - logg->logError(__FILE__, __LINE__, "Invalid session.xml version: %d", version); + logg.logError("Invalid session.xml version: %d", version); handleException(); } @@ -76,13 +77,14 @@ void SessionXML::sessionTag(mxml_node_t *tree, mxml_node_t *node) { strncpy(parameters.sample_rate, mxmlElementGetAttr(node, ATTR_SAMPLE_RATE), sizeof(parameters.sample_rate)); parameters.sample_rate[sizeof(parameters.sample_rate) - 1] = 0; // strncpy does not guarantee a null-terminated string } - if (mxmlElementGetAttr(node, ATTR_CAPTURE_WORKING_DIR)) gSessionData->mCaptureWorkingDir = strdup(mxmlElementGetAttr(node, ATTR_CAPTURE_WORKING_DIR)); - if (mxmlElementGetAttr(node, ATTR_CAPTURE_COMMAND)) gSessionData->mCaptureCommand = strdup(mxmlElementGetAttr(node, ATTR_CAPTURE_COMMAND)); - if (mxmlElementGetAttr(node, ATTR_CAPTURE_USER)) gSessionData->mCaptureUser = strdup(mxmlElementGetAttr(node, ATTR_CAPTURE_USER)); + if (mxmlElementGetAttr(node, ATTR_CAPTURE_WORKING_DIR)) gSessionData.mCaptureWorkingDir = strdup(mxmlElementGetAttr(node, ATTR_CAPTURE_WORKING_DIR)); + if (mxmlElementGetAttr(node, ATTR_CAPTURE_COMMAND)) gSessionData.mCaptureCommand = strdup(mxmlElementGetAttr(node, ATTR_CAPTURE_COMMAND)); + if (mxmlElementGetAttr(node, ATTR_CAPTURE_USER)) gSessionData.mCaptureUser = strdup(mxmlElementGetAttr(node, ATTR_CAPTURE_USER)); // integers/bools - parameters.call_stack_unwinding = util->stringToBool(mxmlElementGetAttr(node, ATTR_CALL_STACK_UNWINDING), false); - if (mxmlElementGetAttr(node, ATTR_DURATION)) gSessionData->mDuration = strtol(mxmlElementGetAttr(node, ATTR_DURATION), NULL, 10); + parameters.call_stack_unwinding = stringToBool(mxmlElementGetAttr(node, ATTR_CALL_STACK_UNWINDING), false); + if (mxmlElementGetAttr(node, ATTR_DURATION)) gSessionData.mDuration = strtol(mxmlElementGetAttr(node, ATTR_DURATION), NULL, 10); + gSessionData.mFtraceRaw = stringToBool(mxmlElementGetAttr(node, USE_EFFICIENT_FTRACE), false); if (mxmlElementGetAttr(node, ATTR_LIVE_RATE)) parameters.live_rate = strtol(mxmlElementGetAttr(node, ATTR_LIVE_RATE), NULL, 10); // parse subtags @@ -106,6 +108,6 @@ void SessionXML::sessionImage(mxml_node_t *node) { image = (struct ImageLinkList *)malloc(sizeof(struct ImageLinkList)); image->path = (char*)malloc(length + 1); image->path = strdup(mxmlElementGetAttr(node, ATTR_PATH)); - image->next = gSessionData->mImages; - gSessionData->mImages = image; + image->next = gSessionData.mImages; + gSessionData.mImages = image; } diff --git a/tools/gator/daemon/SessionXML.h b/tools/gator/daemon/SessionXML.h index 5396574..2ba276a 100644 --- a/tools/gator/daemon/SessionXML.h +++ b/tools/gator/daemon/SessionXML.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 diff --git a/tools/gator/daemon/Setup.cpp b/tools/gator/daemon/Setup.cpp deleted file mode 100644 index d4ce032..0000000 --- a/tools/gator/daemon/Setup.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/** - * Copyright (C) ARM Limited 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 "Setup.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Config.h" -#include "DynBuf.h" -#include "Logging.h" - -bool getLinuxVersion(int version[3]) { - // Check the kernel version - struct utsname utsname; - if (uname(&utsname) != 0) { - logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__); - return false; - } - - version[0] = 0; - version[1] = 0; - version[2] = 0; - - int part = 0; - char *ch = utsname.release; - while (*ch >= '0' && *ch <= '9' && part < 3) { - version[part] = 10*version[part] + *ch - '0'; - - ++ch; - if (*ch == '.') { - ++part; - ++ch; - } - } - - return true; -} - -static int pgrep_gator(DynBuf *const printb) { - DynBuf b; - - DIR *proc = opendir("/proc"); - if (proc == NULL) { - logg->logError(__FILE__, __LINE__, "gator: error: opendir failed"); - handleException(); - } - - int self = getpid(); - - struct dirent *dirent; - while ((dirent = readdir(proc)) != NULL) { - char *endptr; - const int pid = strtol(dirent->d_name, &endptr, 10); - if (*endptr != '\0' || (pid == self)) { - // Ignore proc items that are not integers like ., cpuinfo, etc... - continue; - } - - if (!printb->printf("/proc/%i/stat", pid)) { - logg->logError(__FILE__, __LINE__, "gator: error: DynBuf::printf failed"); - handleException(); - } - - if (!b.read(printb->getBuf())) { - // This is not a fatal error - the thread just doesn't exist any more - continue; - } - - char *comm = strchr(b.getBuf(), '('); - if (comm == NULL) { - logg->logError(__FILE__, __LINE__, "gator: error: parsing stat begin failed"); - handleException(); - } - ++comm; - char *const str = strrchr(comm, ')'); - if (str == NULL) { - logg->logError(__FILE__, __LINE__, "gator: error: parsing stat end failed"); - handleException(); - } - *str = '\0'; - - if (strncmp(comm, "gator", 5) == 0) { - // Assume there is only one gator process - return pid; - } - } - - closedir(proc); - - return -1; -} - -int update(const char *const gatorPath) { - printf("gator: starting\n"); - - int version[3]; - if (!getLinuxVersion(version)) { - logg->logError(__FILE__, __LINE__, "gator: error: getLinuxVersion failed"); - handleException(); - } - - if (KERNEL_VERSION(version[0], version[1], version[2]) < KERNEL_VERSION(2, 6, 32)) { - logg->logError(__FILE__, __LINE__, "gator: error: Streamline can't automatically setup gator as this kernel version is not supported. Please upgrade the kernel on your device."); - handleException(); - } - - if (KERNEL_VERSION(version[0], version[1], version[2]) < KERNEL_VERSION(3, 4, 0)) { - logg->logError(__FILE__, __LINE__, "gator: error: Streamline can't automatically setup gator as gator.ko is required for this version of Linux. Please build gator.ko and gatord and install them on your device."); - handleException(); - } - - if (access("/sys/module/gator", F_OK) == 0) { - logg->logError(__FILE__, __LINE__, "gator: error: Streamline has detected that the gator kernel module is loaded on your device. Please build an updated version of gator.ko and gatord and install them on your device."); - handleException(); - } - - if (geteuid() != 0) { - printf("gator: trying sudo\n"); - execlp("sudo", "sudo", gatorPath, "-u", NULL); - // Streamline will provide the password if needed - - printf("gator: trying su\n"); - char buf[1<<10]; - snprintf(buf, sizeof(buf), "%s -u", gatorPath); - execlp("su", "su", "-", "-c", buf, NULL); - // Streamline will provide the password if needed - - logg->logError(__FILE__, __LINE__, "gator: error: Streamline was unable to sudo to root on your device. Please double check passwords, ensure sudo or su work with this user or try a different username."); - handleException(); - } - printf("gator: now root\n"); - - // setenforce 0 not needed for userspace gator - - // Kill existing gator - DynBuf gatorStatPath; - int gator_main = pgrep_gator(&gatorStatPath); - if (gator_main > 0) { - if (kill(gator_main, SIGTERM) != 0) { - logg->logError(__FILE__, __LINE__, "gator: error: kill SIGTERM failed"); - handleException(); - } - for (int i = 0; ; ++i) { - if (access(gatorStatPath.getBuf(), F_OK) != 0) { - break; - } - if (i == 5) { - if (kill(gator_main, SIGKILL) != 0) { - logg->logError(__FILE__, __LINE__, "gator: error: kill SIGKILL failed"); - handleException(); - } - } else if (i >= 10) { - logg->logError(__FILE__, __LINE__, "gator: error: unable to kill running gator"); - handleException(); - } - sleep(1); - } - } - printf("gator: no gatord running\n"); - - rename("gatord", "gatord.old"); - rename("gator.ko", "gator.ko.old"); - - // Rename gatord.YYYYMMDDHHMMSSMMMM to gatord - char *newGatorPath = strdup(gatorPath); - char *dot = strrchr(newGatorPath, '.'); - if (dot != NULL) { - *dot = '\0'; - if (rename(gatorPath, newGatorPath) != 0) { - logg->logError(__FILE__, __LINE__, "gator: error: rename failed"); - handleException(); - } - } - - // Fork and start gatord (redirect stdout and stderr) - int child = fork(); - if (child < 0) { - logg->logError(__FILE__, __LINE__, "gator: error: fork failed"); - handleException(); - } else if (child == 0) { - int inFd = open("/dev/null", O_RDONLY | O_CLOEXEC); - if (inFd < 0) { - logg->logError(__FILE__, __LINE__, "gator: error: open of /dev/null failed"); - handleException(); - } - int outFd = open("gatord.out", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0600); - if (outFd < 0) { - logg->logError(__FILE__, __LINE__, "gator: error: open of gatord.out failed"); - handleException(); - } - int errFd = open("gatord.err", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0600); - if (errFd < 0) { - logg->logError(__FILE__, __LINE__, "gator: error: open of gatord.err failed"); - handleException(); - } - if (dup2(inFd, STDIN_FILENO) < 0) { - logg->logError(__FILE__, __LINE__, "gator: error: dup2 for stdin failed"); - handleException(); - } - if (dup2(outFd, STDOUT_FILENO) < 0) { - logg->logError(__FILE__, __LINE__, "gator: error: dup2 for stdout failed"); - handleException(); - } - if (dup2(errFd, STDERR_FILENO) < 0) { - logg->logError(__FILE__, __LINE__, "gator: error: dup2 for stderr failed"); - handleException(); - } - execlp(newGatorPath, newGatorPath, "-a", NULL); - logg->logError(__FILE__, __LINE__, "gator: error: execlp failed"); - handleException(); - } - - printf("gator: done\n"); - - return 0; -} diff --git a/tools/gator/daemon/Setup.h b/tools/gator/daemon/Setup.h deleted file mode 100644 index 280d611..0000000 --- a/tools/gator/daemon/Setup.h +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (C) ARM Limited 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. - */ - -#ifndef SETUP_H -#define SETUP_H - -// From include/generated/uapi/linux/version.h -#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) - -bool getLinuxVersion(int version[3]); -int update(const char *const gatorPath); - -#endif // SETUP_H diff --git a/tools/gator/daemon/Source.cpp b/tools/gator/daemon/Source.cpp index 60cf704..3084bcb 100644 --- a/tools/gator/daemon/Source.cpp +++ b/tools/gator/daemon/Source.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 @@ -18,7 +18,7 @@ Source::~Source() { void Source::start() { if (pthread_create(&mThreadID, NULL, runStatic, this)) { - logg->logError(__FILE__, __LINE__, "Failed to create source thread"); + logg.logError("Failed to create source thread"); handleException(); } } diff --git a/tools/gator/daemon/Source.h b/tools/gator/daemon/Source.h index 56ac3d6..b9369be 100644 --- a/tools/gator/daemon/Source.h +++ b/tools/gator/daemon/Source.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 diff --git a/tools/gator/daemon/StreamlineSetup.cpp b/tools/gator/daemon/StreamlineSetup.cpp index 2b61eae..f00d7f1 100644 --- a/tools/gator/daemon/StreamlineSetup.cpp +++ b/tools/gator/daemon/StreamlineSetup.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2014. All rights reserved. + * Copyright (C) ARM Limited 2011-2015. 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 @@ -19,16 +19,16 @@ #include "Sender.h" #include "SessionData.h" -static const char* TAG_SESSION = "session"; -static const char* TAG_REQUEST = "request"; -static const char* TAG_CONFIGURATIONS = "configurations"; +static const char TAG_SESSION[] = "session"; +static const char TAG_REQUEST[] = "request"; +static const char TAG_CONFIGURATIONS[] = "configurations"; -static const char* ATTR_TYPE = "type"; -static const char* VALUE_EVENTS = "events"; -static const char* VALUE_CONFIGURATION = "configuration"; -static const char* VALUE_COUNTERS = "counters"; -static const char* VALUE_CAPTURED = "captured"; -static const char* VALUE_DEFAULTS = "defaults"; +static const char ATTR_TYPE[] = "type"; +static const char VALUE_EVENTS[] = "events"; +static const char VALUE_CONFIGURATION[] = "configuration"; +static const char VALUE_COUNTERS[] = "counters"; +static const char VALUE_CAPTURED[] = "captured"; +static const char VALUE_DEFAULTS[] = "defaults"; StreamlineSetup::StreamlineSetup(OlySocket* s) { bool ready = false; @@ -40,7 +40,7 @@ StreamlineSetup::StreamlineSetup(OlySocket* s) { // Receive commands from Streamline (master) while (!ready) { // receive command over socket - gSessionData->mWaitingOnCommand = true; + gSessionData.mWaitingOnCommand = true; data = readCommand(&type); // parse and handle data @@ -52,31 +52,31 @@ StreamlineSetup::StreamlineSetup(OlySocket* s) { handleDeliver(data); break; case COMMAND_APC_START: - logg->logMessage("Received apc start request"); + logg.logMessage("Received apc start request"); ready = true; break; case COMMAND_APC_STOP: - logg->logMessage("Received apc stop request before apc start request"); + logg.logMessage("Received apc stop request before apc start request"); exit(0); break; case COMMAND_DISCONNECT: - logg->logMessage("Received disconnect command"); + logg.logMessage("Received disconnect command"); exit(0); break; case COMMAND_PING: - logg->logMessage("Received ping command"); + logg.logMessage("Received ping command"); sendData(NULL, 0, RESPONSE_ACK); break; default: - logg->logError(__FILE__, __LINE__, "Target error: Unknown command type, %d", type); + logg.logError("Target error: Unknown command type, %d", type); handleException(); } free(data); } - if (gSessionData->mCounterOverflow > 0) { - logg->logError(__FILE__, __LINE__, "Only %i performance counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, gSessionData->mCounterOverflow); + if (gSessionData.mCountersError != NULL) { + logg.logError("%s", gSessionData.mCountersError); handleException(); } } @@ -93,10 +93,10 @@ char* StreamlineSetup::readCommand(int* command) { response = mSocket->receiveNBytes((char*)&header, sizeof(header)); // After receiving a single byte, we are no longer waiting on a command - gSessionData->mWaitingOnCommand = false; + gSessionData.mWaitingOnCommand = false; if (response < 0) { - logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect"); + logg.logError("Target error: Unexpected socket disconnect"); handleException(); } @@ -105,21 +105,21 @@ char* StreamlineSetup::readCommand(int* command) { // add artificial limit if ((length < 0) || length > 1024 * 1024) { - logg->logError(__FILE__, __LINE__, "Target error: Invalid length received, %d", length); + logg.logError("Target error: Invalid length received, %d", length); handleException(); } // allocate memory to contain the xml file, size of zero returns a zero size object data = (char*)calloc(length + 1, 1); if (data == NULL) { - logg->logError(__FILE__, __LINE__, "Unable to allocate memory for xml"); + logg.logError("Unable to allocate memory for xml"); handleException(); } // receive data response = mSocket->receiveNBytes(data, length); if (response < 0) { - logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect"); + logg.logError("Target error: Unexpected socket disconnect"); handleException(); } @@ -143,26 +143,26 @@ void StreamlineSetup::handleRequest(char* xml) { } if (attr && strcmp(attr, VALUE_EVENTS) == 0) { sendEvents(); - logg->logMessage("Sent events xml response"); + logg.logMessage("Sent events xml response"); } else if (attr && strcmp(attr, VALUE_CONFIGURATION) == 0) { sendConfiguration(); - logg->logMessage("Sent configuration xml response"); + logg.logMessage("Sent configuration xml response"); } else if (attr && strcmp(attr, VALUE_COUNTERS) == 0) { sendCounters(); - logg->logMessage("Sent counters xml response"); + logg.logMessage("Sent counters xml response"); } else if (attr && strcmp(attr, VALUE_CAPTURED) == 0) { CapturedXML capturedXML; char* capturedText = capturedXML.getXML(false); sendData(capturedText, strlen(capturedText), RESPONSE_XML); free(capturedText); - logg->logMessage("Sent captured xml response"); + logg.logMessage("Sent captured xml response"); } else if (attr && strcmp(attr, VALUE_DEFAULTS) == 0) { sendDefaults(); - logg->logMessage("Sent default configuration xml response"); + logg.logMessage("Sent default configuration xml response"); } else { char error[] = "Unknown request"; sendData(error, strlen(error), RESPONSE_NAK); - logg->logMessage("Received unknown request:\n%s", xml); + logg.logMessage("Received unknown request:\n%s", xml); } mxmlDelete(tree); @@ -175,17 +175,17 @@ void StreamlineSetup::handleDeliver(char* xml) { tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK); if (mxmlFindElement(tree, tree, TAG_SESSION, NULL, NULL, MXML_DESCEND_FIRST)) { // Session XML - gSessionData->parseSessionXML(xml); + gSessionData.parseSessionXML(xml); sendData(NULL, 0, RESPONSE_ACK); - logg->logMessage("Received session xml"); + logg.logMessage("Received session xml"); } else if (mxmlFindElement(tree, tree, TAG_CONFIGURATIONS, NULL, NULL, MXML_DESCEND_FIRST)) { // Configuration XML writeConfiguration(xml); sendData(NULL, 0, RESPONSE_ACK); - logg->logMessage("Received configuration xml"); + logg.logMessage("Received configuration xml"); } else { // Unknown XML - logg->logMessage("Received unknown XML delivery type"); + logg.logMessage("Received unknown XML delivery type"); sendData(NULL, 0, RESPONSE_NAK); } @@ -197,7 +197,7 @@ void StreamlineSetup::sendData(const char* data, uint32_t length, char type) { header[0] = type; Buffer::writeLEInt(header + 1, length); mSocket->send((char*)&header, sizeof(header)); - mSocket->send((const char*)data, length); + mSocket->send(data, length); } void StreamlineSetup::sendEvents() { @@ -222,7 +222,7 @@ void StreamlineSetup::sendDefaults() { // Artificial size restriction if (size > 1024*1024) { - logg->logError(__FILE__, __LINE__, "Corrupt default configuration file"); + logg.logError("Corrupt default configuration file"); handleException(); } @@ -240,8 +240,11 @@ void StreamlineSetup::sendCounters() { count += driver->writeCounters(counters); } + mxml_node_t *setup = mxmlNewElement(counters, "setup_warnings"); + mxmlNewText(setup, 0, logg.getSetup()); + if (count == 0) { - logg->logError(__FILE__, __LINE__, "No counters found, this could be because /dev/gator/events can not be read or because perf is not working correctly"); + logg.logError("No counters found, this could be because /dev/gator/events can not be read or because perf is not working correctly"); handleException(); } @@ -257,16 +260,16 @@ void StreamlineSetup::writeConfiguration(char* xml) { ConfigurationXML::getPath(path); - if (util->writeToDisk(path, xml) < 0) { - logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify write permissions to this path.", path); + if (writeToDisk(path, xml) < 0) { + logg.logError("Error writing %s\nPlease verify write permissions to this path.", path); handleException(); } // Re-populate gSessionData with the configuration, as it has now changed { ConfigurationXML configuration; } - if (gSessionData->mCounterOverflow > 0) { - logg->logError(__FILE__, __LINE__, "Only %i performance counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, gSessionData->mCounterOverflow); + if (gSessionData.mCountersError != NULL) { + logg.logError("%s", gSessionData.mCountersError); handleException(); } } diff --git a/tools/gator/daemon/StreamlineSetup.h b/tools/gator/daemon/StreamlineSetup.h index 623e14f..d8b1626 100644 --- a/tools/gator/daemon/StreamlineSetup.h +++ b/tools/gator/daemon/StreamlineSetup.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 diff --git a/tools/gator/daemon/TtraceDriver.cpp b/tools/gator/daemon/TtraceDriver.cpp new file mode 100644 index 0000000..e1ce833 --- /dev/null +++ b/tools/gator/daemon/TtraceDriver.cpp @@ -0,0 +1,132 @@ +/** + * Copyright (C) ARM Limited 2014-2015. 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 "TtraceDriver.h" + +#include +#include +#include +#include +#include + +#include "Logging.h" +#include "OlyUtility.h" +#include "SessionData.h" + +class TtraceCounter : public DriverCounter { +public: + TtraceCounter(DriverCounter *next, char *name, int flag); + ~TtraceCounter(); + + int getFlag() const { return mFlag; } + +private: + const int mFlag; + + // Intentionally unimplemented + TtraceCounter(const TtraceCounter &); + TtraceCounter &operator=(const TtraceCounter &); +}; + +TtraceCounter::TtraceCounter(DriverCounter *next, char *name, int flag) : DriverCounter(next, name), mFlag(flag) { +} + +TtraceCounter::~TtraceCounter() { +} + +TtraceDriver::TtraceDriver() : mSupported(false) { +} + +TtraceDriver::~TtraceDriver() { +} + +void TtraceDriver::readEvents(mxml_node_t *const xml) { + if (access("/etc/tizen-release", R_OK) != 0) { + // Reduce warning noise + //logg.logSetup("Ttrace Disabled\n/etc/tizen-release is not found, this is not a Tizen target"); + return; + } + if (!gSessionData.mFtraceDriver.isSupported()) { + logg.logSetup("Ttrace Disabled\nftrace support is required"); + return; + } + + mSupported = true; + + mxml_node_t *node = xml; + while (true) { + node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND); + if (node == NULL) { + break; + } + const char *counter = mxmlElementGetAttr(node, "counter"); + if (counter == NULL) { + continue; + } + + if (strncmp(counter, "ttrace_", 7) != 0) { + continue; + } + + const char *flag = mxmlElementGetAttr(node, "flag"); + if (flag == NULL) { + logg.logError("The ttrace counter %s is missing the required flag attribute", counter); + handleException(); + } + setCounters(new TtraceCounter(getCounters(), strdup(counter), strtol(flag, NULL, 16))); + } +} + +void TtraceDriver::setTtrace(const int flags) { + logg.logMessage("Setting ttrace flags to %i", flags); + + const int fd = open("/tmp/ttrace_tag", O_CREAT | O_RDWR | O_CLOEXEC, 0666); + if (fd < 0) { + logg.logError("Unable to open /tmp/ttrace_tag"); + handleException(); + } + if (ftruncate(fd, sizeof(uint64_t)) != 0) { + logg.logError("ftruncate failed"); + handleException(); + } + + uint64_t *const buf = (uint64_t *)mmap(NULL, sizeof(uint64_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (buf == MAP_FAILED) { + logg.logError("mmap failed"); + handleException(); + } + close(fd); + + *buf = flags; + + munmap(buf, sizeof(uint64_t)); +} + +void TtraceDriver::start() { + if (!mSupported) { + return; + } + + int flags = 0; + for (TtraceCounter *counter = static_cast(getCounters()); counter != NULL; counter = static_cast(counter->getNext())) { + if (!counter->isEnabled()) { + continue; + } + flags |= counter->getFlag(); + } + + setTtrace(flags); +} + +void TtraceDriver::stop() { + if (!mSupported) { + return; + } + + setTtrace(0); +} diff --git a/tools/gator/daemon/TtraceDriver.h b/tools/gator/daemon/TtraceDriver.h new file mode 100644 index 0000000..86a0630 --- /dev/null +++ b/tools/gator/daemon/TtraceDriver.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) ARM Limited 2015. 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. + */ + +#ifndef TTRACEDRIVER_H +#define TTRACEDRIVER_H + +#include "mxml/mxml.h" + +#include "Driver.h" + +class TtraceDriver : public SimpleDriver { +public: + TtraceDriver(); + ~TtraceDriver(); + + void readEvents(mxml_node_t *const xml); + + void start(); + void stop(); + + bool isSupported() const { return mSupported; } + +private: + void setTtrace(const int flags); + + bool mSupported; + + // Intentionally unimplemented + TtraceDriver(const TtraceDriver &); + TtraceDriver &operator=(const TtraceDriver &); +}; + +#endif // TTRACEDRIVER_H diff --git a/tools/gator/daemon/UEvent.cpp b/tools/gator/daemon/UEvent.cpp index f94a995..3b0447a 100644 --- a/tools/gator/daemon/UEvent.cpp +++ b/tools/gator/daemon/UEvent.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 @@ -34,7 +34,7 @@ UEvent::~UEvent() { bool UEvent::init() { mFd = socket_cloexec(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT); if (mFd < 0) { - logg->logMessage("%s(%s:%i): socket failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("socket failed"); return false; } @@ -44,7 +44,7 @@ bool UEvent::init() { sockaddr.nl_groups = 1; // bitmask: (1 << 0) == kernel events, (1 << 1) == udev events sockaddr.nl_pid = 0; if (bind(mFd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != 0) { - logg->logMessage("%s(%s:%i): bind failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("bind failed"); return false; } @@ -54,7 +54,7 @@ bool UEvent::init() { bool UEvent::read(UEventResult *const result) { ssize_t bytes = recv(mFd, result->mBuf, sizeof(result->mBuf), 0); if (bytes <= 0) { - logg->logMessage("%s(%s:%i): recv failed", __FUNCTION__, __FILE__, __LINE__); + logg.logMessage("recv failed"); return false; } @@ -64,6 +64,7 @@ bool UEvent::read(UEventResult *const result) { for (int pos = 0; pos < bytes; pos += strlen(result->mBuf + pos) + 1) { char *const str = result->mBuf + pos; + logg.logMessage("uevent + %i: %s", pos, str); if (strncmp(str, ACTION, sizeof(ACTION) - 1) == 0) { result->mAction = str + sizeof(ACTION) - 1; } else if (strncmp(str, DEVPATH, sizeof(DEVPATH) - 1) == 0) { diff --git a/tools/gator/daemon/UEvent.h b/tools/gator/daemon/UEvent.h index 2f7ef2c..4c00f6c 100644 --- a/tools/gator/daemon/UEvent.h +++ b/tools/gator/daemon/UEvent.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * Copyright (C) ARM Limited 2013-2015. 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 diff --git a/tools/gator/daemon/UserSpaceSource.cpp b/tools/gator/daemon/UserSpaceSource.cpp index 4a9b22f..6b9565b 100644 --- a/tools/gator/daemon/UserSpaceSource.cpp +++ b/tools/gator/daemon/UserSpaceSource.cpp @@ -1,13 +1,16 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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. */ +#define __STDC_FORMAT_MACROS + #include "UserSpaceSource.h" +#include #include #include @@ -18,7 +21,7 @@ extern Child *child; -UserSpaceSource::UserSpaceSource(sem_t *senderSem) : mBuffer(0, FRAME_BLOCK_COUNTER, gSessionData->mTotalBufferSize*1024*1024, senderSem) { +UserSpaceSource::UserSpaceSource(sem_t *senderSem) : mBuffer(0, FRAME_BLOCK_COUNTER, gSessionData.mTotalBufferSize*1024*1024, senderSem) { } UserSpaceSource::~UserSpaceSource() { @@ -31,49 +34,49 @@ bool UserSpaceSource::prepare() { void UserSpaceSource::run() { prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0); - for (int i = 0; i < ARRAY_LENGTH(gSessionData->usDrivers); ++i) { - gSessionData->usDrivers[i]->start(); + for (int i = 0; i < ARRAY_LENGTH(gSessionData.mUsDrivers); ++i) { + gSessionData.mUsDrivers[i]->start(); } - int64_t monotonic_started = 0; - while (monotonic_started <= 0) { + int64_t monotonicStarted = 0; + while (monotonicStarted <= 0 && gSessionData.mSessionIsActive) { usleep(10); - if (gSessionData->perf.isSetup()) { - monotonic_started = gSessionData->mMonotonicStarted; + if (gSessionData.mPerf.isSetup()) { + monotonicStarted = gSessionData.mMonotonicStarted; } else { - if (DriverSource::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) { - logg->logError(__FILE__, __LINE__, "Error reading gator driver start time"); + if (DriverSource::readInt64Driver("/dev/gator/started", &monotonicStarted) == -1) { + logg.logError("Error reading gator driver start time"); handleException(); } - gSessionData->mMonotonicStarted = monotonic_started; + gSessionData.mMonotonicStarted = monotonicStarted; } } - uint64_t next_time = 0; - while (gSessionData->mSessionIsActive) { - const uint64_t curr_time = getTime() - monotonic_started; - // Sample ten times a second ignoring gSessionData->mSampleRate - next_time += NS_PER_S/10;//gSessionData->mSampleRate; - if (next_time < curr_time) { - logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time); - next_time = curr_time; + uint64_t nextTime = 0; + while (gSessionData.mSessionIsActive) { + const uint64_t currTime = getTime() - monotonicStarted; + // Sample ten times a second ignoring gSessionData.mSampleRate + nextTime += NS_PER_S/10;//gSessionData.mSampleRate; + if (nextTime < currTime) { + logg.logMessage("Too slow, currTime: %" PRIi64 " nextTime: %" PRIi64, currTime, nextTime); + nextTime = currTime; } - if (mBuffer.eventHeader(curr_time)) { - for (int i = 0; i < ARRAY_LENGTH(gSessionData->usDrivers); ++i) { - gSessionData->usDrivers[i]->read(&mBuffer); + if (mBuffer.eventHeader(currTime)) { + for (int i = 0; i < ARRAY_LENGTH(gSessionData.mUsDrivers); ++i) { + gSessionData.mUsDrivers[i]->read(&mBuffer); } // Only check after writing all counters so that time and corresponding counters appear in the same frame - mBuffer.check(curr_time); + mBuffer.check(currTime); } - if (mBuffer.bytesAvailable() <= 0) { - logg->logMessage("One shot (counters)"); + if (gSessionData.mOneShot && gSessionData.mSessionIsActive && (mBuffer.bytesAvailable() <= 0)) { + logg.logMessage("One shot (counters)"); child->endSession(); } - usleep((next_time - curr_time)/NS_PER_US); + usleep((nextTime - currTime)/NS_PER_US); } mBuffer.setDone(); diff --git a/tools/gator/daemon/UserSpaceSource.h b/tools/gator/daemon/UserSpaceSource.h index 9b36660..0038dcb 100644 --- a/tools/gator/daemon/UserSpaceSource.h +++ b/tools/gator/daemon/UserSpaceSource.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 diff --git a/tools/gator/daemon/c++.cpp b/tools/gator/daemon/c++.cpp index 6041e5e..caf6f1e 100644 --- a/tools/gator/daemon/c++.cpp +++ b/tools/gator/daemon/c++.cpp @@ -1,7 +1,7 @@ /** * Minimal set of C++ functions so that libstdc++ is not required * - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 diff --git a/tools/gator/daemon/common.mk b/tools/gator/daemon/common.mk index 769a92e..344abd2 100644 --- a/tools/gator/daemon/common.mk +++ b/tools/gator/daemon/common.mk @@ -5,8 +5,8 @@ # -Werror treats warnings as errors # -std=c++0x is the planned new c++ standard # -std=c++98 is the 1998 c++ standard -CPPFLAGS += -O3 -Wall -fno-exceptions -pthread -MMD -DETCDIR=\"/etc\" -Ilibsensors -CXXFLAGS += -fno-rtti -Wextra # -Weffc++ +CPPFLAGS += -O3 -Wall -fno-exceptions -pthread -MD -DETCDIR=\"/etc\" -Ilibsensors +CXXFLAGS += -fno-rtti -Wextra -Wshadow # -Weffc++ ifeq ($(WERROR),1) CPPFLAGS += -Werror endif @@ -24,9 +24,11 @@ events.xml: events_header.xml $(wildcard events-*.xml) events_footer.xml include $(wildcard *.d) include $(wildcard mxml/*.d) +include $(wildcard libsensors/*.d) EventsXML.cpp: events_xml.h ConfigurationXML.cpp: defaults_xml.h +PmuXML.cpp: pmus_xml.h # Don't regenerate conf-lex.c or conf-parse.c libsensors/conf-lex.c: ; @@ -41,7 +43,10 @@ libsensors/conf-parse.c: ; %.o: %.cpp $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< -$(TARGET): $(CXX_SRC:%.cpp=%.o) $(C_SRC:%.c=%.o) +SrcMd5.cpp: $(wildcard *.cpp *.h mxml/*.c mxml/*.h libsensors/*.c libsensors/*.h) + echo 'extern const char *const gSrcMd5 = "'`ls $^ | grep -Ev '^(.*_xml\.h|$@)$$' | LC_ALL=C sort | xargs cat | md5sum | cut -b 1-32`'";' > $@ + +$(TARGET): $(CXX_SRC:%.cpp=%.o) $(C_SRC:%.c=%.o) SrcMd5.o $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@ # Intentionally ignore CC as a native binary is required @@ -49,4 +54,4 @@ escape: escape.c gcc $^ -o $@ clean: - rm -f *.d *.o mxml/*.d mxml/*.o libsensors/*.d libsensors/*.o $(TARGET) escape events.xml events_xml.h defaults_xml.h + rm -f *.d *.o mxml/*.d mxml/*.o libsensors/*.d libsensors/*.o $(TARGET) escape events.xml *_xml.h SrcMd5.cpp diff --git a/tools/gator/daemon/defaults.xml b/tools/gator/daemon/defaults.xml index 086eca1..5d0d620 100644 --- a/tools/gator/daemon/defaults.xml +++ b/tools/gator/daemon/defaults.xml @@ -34,16 +34,31 @@ - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -59,26 +74,12 @@ + + - - - - - - - - - - - - - - - - diff --git a/tools/gator/daemon/defaults_xml.h b/tools/gator/daemon/defaults_xml.h deleted file mode 100644 index 5e999fe..0000000 --- a/tools/gator/daemon/defaults_xml.h +++ /dev/null @@ -1,407 +0,0 @@ -static const unsigned char defaults_xml[] = { - 0x3c, 0x3f, 0x78, 0x6d, 0x6c, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x3d, 0x22, 0x31, 0x2e, 0x30, 0x22, 0x20, 0x65, 0x6e, 0x63, 0x6f, - 0x64, 0x69, 0x6e, 0x67, 0x3d, 0x22, 0x55, 0x54, 0x46, 0x2d, 0x38, 0x22, - 0x3f, 0x3e, 0x0a, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x33, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x3c, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, - 0x52, 0x4d, 0x5f, 0x41, 0x52, 0x4d, 0x31, 0x31, 0x5f, 0x63, 0x63, 0x6e, - 0x74, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, - 0x66, 0x66, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, - 0x41, 0x52, 0x4d, 0x31, 0x31, 0x5f, 0x63, 0x6e, 0x74, 0x30, 0x22, 0x20, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x37, 0x22, 0x2f, - 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x41, 0x52, 0x4d, 0x31, - 0x31, 0x5f, 0x63, 0x6e, 0x74, 0x31, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x3d, 0x22, 0x30, 0x78, 0x62, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, - 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, - 0x41, 0x52, 0x4d, 0x5f, 0x41, 0x52, 0x4d, 0x31, 0x31, 0x4d, 0x50, 0x43, - 0x6f, 0x72, 0x65, 0x5f, 0x63, 0x63, 0x6e, 0x74, 0x22, 0x20, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x66, 0x66, 0x22, 0x2f, 0x3e, - 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, - 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x41, 0x52, 0x4d, 0x31, 0x31, - 0x4d, 0x50, 0x43, 0x6f, 0x72, 0x65, 0x5f, 0x63, 0x6e, 0x74, 0x30, 0x22, - 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x30, 0x38, - 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x41, 0x52, - 0x4d, 0x31, 0x31, 0x4d, 0x50, 0x43, 0x6f, 0x72, 0x65, 0x5f, 0x63, 0x6e, - 0x74, 0x31, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, - 0x78, 0x30, 0x62, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, - 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x41, 0x35, - 0x5f, 0x63, 0x63, 0x6e, 0x74, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x3d, 0x22, 0x30, 0x78, 0x66, 0x66, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, - 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, - 0x41, 0x52, 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, - 0x5f, 0x41, 0x35, 0x5f, 0x63, 0x6e, 0x74, 0x30, 0x22, 0x20, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x38, 0x22, 0x2f, 0x3e, 0x0a, - 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, - 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, 0x74, - 0x65, 0x78, 0x5f, 0x41, 0x35, 0x5f, 0x63, 0x6e, 0x74, 0x31, 0x22, 0x20, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x31, 0x22, 0x2f, - 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, - 0x72, 0x74, 0x65, 0x78, 0x5f, 0x41, 0x37, 0x5f, 0x63, 0x63, 0x6e, 0x74, - 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x66, - 0x66, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x76, 0x37, - 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x41, 0x37, 0x5f, 0x63, - 0x6e, 0x74, 0x30, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, - 0x30, 0x78, 0x30, 0x38, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, - 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x41, - 0x37, 0x5f, 0x63, 0x6e, 0x74, 0x31, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x3d, 0x22, 0x30, 0x78, 0x31, 0x30, 0x22, 0x2f, 0x3e, 0x0a, 0x20, - 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, - 0x22, 0x41, 0x52, 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, - 0x78, 0x5f, 0x41, 0x37, 0x5f, 0x63, 0x6e, 0x74, 0x32, 0x22, 0x20, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x31, 0x36, 0x22, 0x2f, - 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, - 0x72, 0x74, 0x65, 0x78, 0x5f, 0x41, 0x38, 0x5f, 0x63, 0x63, 0x6e, 0x74, - 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x66, - 0x66, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x76, 0x37, - 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x41, 0x38, 0x5f, 0x63, - 0x6e, 0x74, 0x30, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, - 0x30, 0x78, 0x38, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, - 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x41, 0x38, - 0x5f, 0x63, 0x6e, 0x74, 0x31, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x3d, 0x22, 0x30, 0x78, 0x34, 0x34, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, - 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, - 0x41, 0x52, 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, - 0x5f, 0x41, 0x38, 0x5f, 0x63, 0x6e, 0x74, 0x32, 0x22, 0x20, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x34, 0x33, 0x22, 0x2f, 0x3e, - 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, - 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, - 0x74, 0x65, 0x78, 0x5f, 0x41, 0x38, 0x5f, 0x63, 0x6e, 0x74, 0x33, 0x22, - 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x31, 0x30, - 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x76, 0x37, 0x5f, - 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x41, 0x39, 0x5f, 0x63, 0x63, - 0x6e, 0x74, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, - 0x78, 0x66, 0x66, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, - 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x41, 0x39, - 0x5f, 0x63, 0x6e, 0x74, 0x30, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x3d, 0x22, 0x30, 0x78, 0x36, 0x38, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, - 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, - 0x41, 0x52, 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, - 0x5f, 0x41, 0x39, 0x5f, 0x63, 0x6e, 0x74, 0x31, 0x22, 0x20, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x30, 0x36, 0x22, 0x2f, 0x3e, - 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, - 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, - 0x74, 0x65, 0x78, 0x5f, 0x41, 0x39, 0x5f, 0x63, 0x6e, 0x74, 0x32, 0x22, - 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x30, 0x37, - 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x76, 0x37, 0x5f, - 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x41, 0x39, 0x5f, 0x63, 0x6e, - 0x74, 0x33, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, - 0x78, 0x30, 0x33, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, - 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x41, 0x39, - 0x5f, 0x63, 0x6e, 0x74, 0x34, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x3d, 0x22, 0x30, 0x78, 0x30, 0x34, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, - 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, - 0x41, 0x52, 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, - 0x5f, 0x41, 0x31, 0x35, 0x5f, 0x63, 0x63, 0x6e, 0x74, 0x22, 0x20, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x66, 0x66, 0x22, 0x2f, - 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, - 0x72, 0x74, 0x65, 0x78, 0x5f, 0x41, 0x31, 0x35, 0x5f, 0x63, 0x6e, 0x74, - 0x30, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, - 0x38, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x76, 0x37, - 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x41, 0x31, 0x35, 0x5f, - 0x63, 0x6e, 0x74, 0x31, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, - 0x22, 0x30, 0x78, 0x31, 0x36, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, - 0x52, 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x5f, - 0x41, 0x31, 0x35, 0x5f, 0x63, 0x6e, 0x74, 0x32, 0x22, 0x20, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x31, 0x30, 0x22, 0x2f, 0x3e, - 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, - 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, - 0x74, 0x65, 0x78, 0x5f, 0x41, 0x31, 0x35, 0x5f, 0x63, 0x6e, 0x74, 0x33, - 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x31, - 0x39, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x76, 0x37, - 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x41, 0x31, 0x37, 0x5f, - 0x63, 0x63, 0x6e, 0x74, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, - 0x22, 0x30, 0x78, 0x66, 0x66, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, - 0x52, 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x5f, - 0x41, 0x31, 0x37, 0x5f, 0x63, 0x6e, 0x74, 0x30, 0x22, 0x20, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x30, 0x38, 0x22, 0x2f, 0x3e, - 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, - 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, - 0x74, 0x65, 0x78, 0x5f, 0x41, 0x31, 0x37, 0x5f, 0x63, 0x6e, 0x74, 0x31, - 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x31, - 0x36, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x76, 0x37, - 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x41, 0x31, 0x37, 0x5f, - 0x63, 0x6e, 0x74, 0x32, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, - 0x22, 0x30, 0x78, 0x31, 0x30, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, - 0x52, 0x4d, 0x76, 0x37, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x5f, - 0x41, 0x31, 0x37, 0x5f, 0x63, 0x6e, 0x74, 0x33, 0x22, 0x20, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x31, 0x39, 0x22, 0x2f, 0x3e, - 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, - 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, - 0x78, 0x2d, 0x41, 0x35, 0x33, 0x5f, 0x63, 0x63, 0x6e, 0x74, 0x22, 0x20, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x31, 0x31, 0x22, - 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x43, 0x6f, 0x72, - 0x74, 0x65, 0x78, 0x2d, 0x41, 0x35, 0x33, 0x5f, 0x63, 0x6e, 0x74, 0x30, - 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x38, - 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x43, 0x6f, - 0x72, 0x74, 0x65, 0x78, 0x2d, 0x41, 0x35, 0x33, 0x5f, 0x63, 0x6e, 0x74, - 0x31, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, - 0x31, 0x36, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, - 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x2d, 0x41, 0x35, 0x33, 0x5f, 0x63, - 0x6e, 0x74, 0x32, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, - 0x30, 0x78, 0x31, 0x30, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, - 0x4d, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x2d, 0x41, 0x35, 0x33, - 0x5f, 0x63, 0x6e, 0x74, 0x33, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x3d, 0x22, 0x30, 0x78, 0x31, 0x39, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, - 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, - 0x41, 0x52, 0x4d, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, 0x2d, 0x41, - 0x35, 0x37, 0x5f, 0x63, 0x63, 0x6e, 0x74, 0x22, 0x20, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x31, 0x31, 0x22, 0x2f, 0x3e, 0x0a, - 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, - 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, 0x78, - 0x2d, 0x41, 0x35, 0x37, 0x5f, 0x63, 0x6e, 0x74, 0x30, 0x22, 0x20, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x38, 0x22, 0x2f, 0x3e, - 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, - 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x43, 0x6f, 0x72, 0x74, 0x65, - 0x78, 0x2d, 0x41, 0x35, 0x37, 0x5f, 0x63, 0x6e, 0x74, 0x31, 0x22, 0x20, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x31, 0x36, 0x22, - 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x43, 0x6f, 0x72, - 0x74, 0x65, 0x78, 0x2d, 0x41, 0x35, 0x37, 0x5f, 0x63, 0x6e, 0x74, 0x32, - 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x31, - 0x30, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x43, - 0x6f, 0x72, 0x74, 0x65, 0x78, 0x2d, 0x41, 0x35, 0x37, 0x5f, 0x63, 0x6e, - 0x74, 0x33, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, - 0x78, 0x31, 0x39, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x53, 0x63, 0x6f, - 0x72, 0x70, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x63, 0x6e, 0x74, 0x22, 0x20, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x66, 0x66, 0x22, - 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x65, 0x72, 0x3d, 0x22, 0x53, 0x63, 0x6f, 0x72, 0x70, 0x69, 0x6f, - 0x6e, 0x5f, 0x63, 0x6e, 0x74, 0x30, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x3d, 0x22, 0x30, 0x78, 0x30, 0x38, 0x22, 0x2f, 0x3e, 0x0a, 0x20, - 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, - 0x22, 0x53, 0x63, 0x6f, 0x72, 0x70, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6e, - 0x74, 0x31, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, - 0x78, 0x31, 0x30, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x53, 0x63, 0x6f, - 0x72, 0x70, 0x69, 0x6f, 0x6e, 0x4d, 0x50, 0x5f, 0x63, 0x63, 0x6e, 0x74, - 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x66, - 0x66, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x53, 0x63, 0x6f, 0x72, 0x70, - 0x69, 0x6f, 0x6e, 0x4d, 0x50, 0x5f, 0x63, 0x6e, 0x74, 0x30, 0x22, 0x20, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x30, 0x38, 0x22, - 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x65, 0x72, 0x3d, 0x22, 0x53, 0x63, 0x6f, 0x72, 0x70, 0x69, 0x6f, - 0x6e, 0x4d, 0x50, 0x5f, 0x63, 0x6e, 0x74, 0x31, 0x22, 0x20, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x31, 0x30, 0x22, 0x2f, 0x3e, - 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, - 0x72, 0x3d, 0x22, 0x4b, 0x72, 0x61, 0x69, 0x74, 0x5f, 0x63, 0x63, 0x6e, - 0x74, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, - 0x66, 0x66, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x4b, 0x72, 0x61, 0x69, - 0x74, 0x5f, 0x63, 0x6e, 0x74, 0x30, 0x22, 0x20, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x3d, 0x22, 0x30, 0x78, 0x30, 0x38, 0x22, 0x2f, 0x3e, 0x0a, 0x20, - 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, - 0x22, 0x4b, 0x72, 0x61, 0x69, 0x74, 0x5f, 0x63, 0x6e, 0x74, 0x31, 0x22, - 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x31, 0x30, - 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x5f, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x71, 0x5f, 0x77, 0x72, 0x22, - 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x65, 0x72, 0x3d, 0x22, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x5f, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x71, 0x5f, 0x72, 0x64, 0x22, 0x2f, - 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x65, 0x72, 0x3d, 0x22, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x5f, 0x6d, 0x65, - 0x6d, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x6d, 0x65, 0x6d, 0x75, 0x73, 0x65, - 0x64, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x4c, 0x69, 0x6e, 0x75, 0x78, - 0x5f, 0x6d, 0x65, 0x6d, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x6d, 0x65, 0x6d, - 0x75, 0x73, 0x65, 0x64, 0x32, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x4c, - 0x69, 0x6e, 0x75, 0x78, 0x5f, 0x6d, 0x65, 0x6d, 0x69, 0x6e, 0x66, 0x6f, - 0x5f, 0x6d, 0x65, 0x6d, 0x66, 0x72, 0x65, 0x65, 0x22, 0x2f, 0x3e, 0x0a, - 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, - 0x3d, 0x22, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x5f, 0x70, 0x6f, 0x77, 0x65, - 0x72, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x66, 0x72, 0x65, 0x71, 0x22, 0x2f, - 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, 0x61, 0x6c, 0x69, - 0x2d, 0x34, 0x78, 0x78, 0x5f, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, - 0x74, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, - 0x61, 0x6c, 0x69, 0x2d, 0x34, 0x78, 0x78, 0x5f, 0x76, 0x65, 0x72, 0x74, - 0x65, 0x78, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, - 0x4d, 0x61, 0x6c, 0x69, 0x2d, 0x4d, 0x69, 0x64, 0x67, 0x61, 0x72, 0x64, - 0x5f, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x20, 0x63, - 0x6f, 0x72, 0x65, 0x73, 0x3d, 0x22, 0x31, 0x22, 0x2f, 0x3e, 0x0a, 0x20, - 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, - 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, 0x61, 0x6c, 0x69, 0x2d, 0x4d, 0x69, - 0x64, 0x67, 0x61, 0x72, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, - 0x22, 0x20, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x3d, 0x22, 0x31, 0x22, 0x2f, - 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, 0x61, 0x6c, 0x69, - 0x2d, 0x4d, 0x69, 0x64, 0x67, 0x61, 0x72, 0x64, 0x5f, 0x6f, 0x70, 0x65, - 0x6e, 0x63, 0x6c, 0x22, 0x20, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x3d, 0x22, - 0x31, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, - 0x61, 0x6c, 0x69, 0x2d, 0x54, 0x36, 0x30, 0x78, 0x5f, 0x47, 0x50, 0x55, - 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x22, 0x2f, 0x3e, 0x0a, 0x20, - 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, - 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, 0x61, 0x6c, 0x69, 0x2d, 0x54, 0x36, - 0x30, 0x78, 0x5f, 0x4a, 0x53, 0x30, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, - 0x45, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, - 0x61, 0x6c, 0x69, 0x2d, 0x54, 0x36, 0x30, 0x78, 0x5f, 0x4a, 0x53, 0x31, - 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x22, 0x2f, 0x3e, 0x0a, 0x20, - 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, - 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, 0x61, 0x6c, 0x69, 0x2d, 0x54, 0x36, - 0x30, 0x78, 0x5f, 0x4a, 0x53, 0x32, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, - 0x45, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, - 0x61, 0x6c, 0x69, 0x2d, 0x54, 0x36, 0x32, 0x78, 0x5f, 0x47, 0x50, 0x55, - 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x22, 0x2f, 0x3e, 0x0a, 0x20, - 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, - 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, 0x61, 0x6c, 0x69, 0x2d, 0x54, 0x36, - 0x32, 0x78, 0x5f, 0x4a, 0x53, 0x30, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, - 0x45, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, - 0x61, 0x6c, 0x69, 0x2d, 0x54, 0x36, 0x32, 0x78, 0x5f, 0x4a, 0x53, 0x31, - 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x22, 0x2f, 0x3e, 0x0a, 0x20, - 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, - 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, 0x61, 0x6c, 0x69, 0x2d, 0x54, 0x36, - 0x32, 0x78, 0x5f, 0x4a, 0x53, 0x32, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, - 0x45, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, - 0x61, 0x6c, 0x69, 0x2d, 0x54, 0x37, 0x32, 0x78, 0x5f, 0x47, 0x50, 0x55, - 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x22, 0x2f, 0x3e, 0x0a, 0x20, - 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, - 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, 0x61, 0x6c, 0x69, 0x2d, 0x54, 0x37, - 0x32, 0x78, 0x5f, 0x4a, 0x53, 0x30, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, - 0x45, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, - 0x61, 0x6c, 0x69, 0x2d, 0x54, 0x37, 0x32, 0x78, 0x5f, 0x4a, 0x53, 0x31, - 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x22, 0x2f, 0x3e, 0x0a, 0x20, - 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, - 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, 0x61, 0x6c, 0x69, 0x2d, 0x54, 0x37, - 0x32, 0x78, 0x5f, 0x4a, 0x53, 0x32, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, - 0x45, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, - 0x61, 0x6c, 0x69, 0x2d, 0x54, 0x37, 0x36, 0x78, 0x5f, 0x47, 0x50, 0x55, - 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x22, 0x2f, 0x3e, 0x0a, 0x20, - 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, - 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, 0x61, 0x6c, 0x69, 0x2d, 0x54, 0x37, - 0x36, 0x78, 0x5f, 0x4a, 0x53, 0x30, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, - 0x45, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, - 0x61, 0x6c, 0x69, 0x2d, 0x54, 0x37, 0x36, 0x78, 0x5f, 0x4a, 0x53, 0x31, - 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x22, 0x2f, 0x3e, 0x0a, 0x20, - 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, - 0x22, 0x41, 0x52, 0x4d, 0x5f, 0x4d, 0x61, 0x6c, 0x69, 0x2d, 0x54, 0x37, - 0x36, 0x78, 0x5f, 0x4a, 0x53, 0x32, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, - 0x45, 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3d, 0x22, 0x4c, 0x32, 0x43, 0x2d, 0x33, - 0x31, 0x30, 0x5f, 0x63, 0x6e, 0x74, 0x30, 0x22, 0x20, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x3d, 0x22, 0x30, 0x78, 0x31, 0x22, 0x2f, 0x3e, 0x0a, 0x3c, - 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x3e, 0x0a, 0x00 -}; -static const unsigned int defaults_xml_len = 4841; diff --git a/tools/gator/daemon/escape.c b/tools/gator/daemon/escape.c index 2b0863a..99f4348 100644 --- a/tools/gator/daemon/escape.c +++ b/tools/gator/daemon/escape.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * Copyright (C) ARM Limited 2010-2015. 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 diff --git a/tools/gator/daemon/events-ARM11.xml b/tools/gator/daemon/events-ARM11.xml index 57e3235..e481267 100644 --- a/tools/gator/daemon/events-ARM11.xml +++ b/tools/gator/daemon/events-ARM11.xml @@ -16,7 +16,7 @@ - + diff --git a/tools/gator/daemon/events-CCI-400.xml b/tools/gator/daemon/events-CCI-400.xml index 20002ef..0dd72c0 100644 --- a/tools/gator/daemon/events-CCI-400.xml +++ b/tools/gator/daemon/events-CCI-400.xml @@ -1,5 +1,5 @@ - + - - - + + +