11

我想知道是否有一种监视我的应用程序内部的好方法,最好是以现有库的形式。

我的应用程序是多线程的,并且使用消息系统在线程之间和与外部世界进行通信。我的目标是监控发送的消息类型、频率等。

还可能有其他更通用的统计信息,例如每分钟产生多少线程,调用了多少新/删除,或更具体的应用程序方面;你的名字。

令人敬畏的是您为 Google Chrome 提供的“内部页面”,例如net或 chrome://tracing ,但以命令行方式。

如果有一个足够通用的库来适应我的应用程序的特殊性,那就太好了。
否则我准备实现一个可以完成这项工作的小类,但我不知道从哪里开始。我认为最重要的是代码不应该过多干扰,以免影响性能。

你们对这个问题有什么建议吗?

编辑:我的应用程序在 Linux 上运行,在嵌入式环境中,遗憾的是 Valgrind 不支持 :(

4

7 回答 7

3

我猜你正在尝试收集运行时统计信息——比如你发送了多少字节,你已经运行了多长时间,以及用户激活了特定功能的次数。

通常,为了从各种来源(如工作线程)编译诸如此类的运行时统计数据,我会让每个来源(线程)增加自己的最基本数据的本地计数器,但不执行任何冗长的数学运算或尚未对该数据进行分析。

然后回到主线程(或者你想要分析和显示这些统计数据的任何地方),我向RequestProgress每个工作线程发送一条类型消息。作为响应,工作线程将收集所有基本数据并可能执行一些简单的分析。这些数据连同基本分析的结果一起以ProgressReport消息的形式发送回请求(主)线程。然后主线程聚合所有这些数据,进行额外的(可能是昂贵的)分析、格式化和显示给用户或记录。

主线程RequestProgress根据用户请求(例如当他们按下S键时)或按时间间隔发送此消息。如果我想要一个定时间隔,我通常会实现另一个新的“心跳”线程。这个线程所做的只是Sleep()在指定的时间内,然后向Heartbeat主线程发送一条消息。主线程反过来通过向每个要从中收集统计信息的工作线程Heartbeat发送消息来处理此消息。RequestProgress

收集统计数据的行为似乎应该相当简单。那么为什么会有如此复杂的机制呢?答案是双重的。

首先,工作线程有工作要做,而不是计算使用统计。试图重构这些线程以承担与其主要目的正交的第二个责任有点像试图将一个方形钉塞进一个圆孔。它们不是为此而构建的,因此代码将抵制被编写。

其次,如果您尝试做太多、太频繁,则运行时统计的计算可能会很昂贵。例如,假设您有一个在网络上发送多播数据的工作线程,并且您想要收集吞吐量数据。多少字节,在多长时间内,平均每秒多少字节。您可以让工作线程自行计算所有这些,但工作量很大,而且 CPU 时间最好由工作线程来做它应该做的事情——发送多播数据。相反,如果您只是为每次发送消息时发送的字节数增加一个计数器,则计数对线程性能的影响很小。然后回应偶尔的RequestProgress消息,您可以计算出开始和停止时间,然后将其发送以让主线程完成所有划分等。

于 2012-07-06T16:05:43.083 回答
3

我建议在您的代码中,维护递增的计数器。计数器可以是static类成员或全局变量。如果您使用类来定义您的计数器,您可以让构造函数将您的计数器注册到单个存储库以及名称。然后,您可以通过查询存储库来查询和重置您的计数器。

struct Counter {
    unsigned long c_;
    unsigned long operator++ () { return ++c_; }
    operator unsigned long () const { return c_; }
    void reset () { unsigned long c = c_; ATOMIC_DECREMENT(c_, c); }
    Counter (std::string name);
};

struct CounterAtomic : public Counter {
    unsigned long operator++ () { return ATOMIC_INCREMENT(c_, 1); }
    CounterAtomic (std::string name) : Counter(name) {}
};

ATOMIC_INCREMENT将是一种特定于平台的机制,可以原子地增加计数器。__sync_add_and_fetchGCC为此提供了一个内置的。ATOMIC_DECREMENT类似,内置 GCC __sync_sub_and_fetch

struct CounterRepository {
    typedef std::map<std::string, Counter *> MapType;
    mutable Mutex lock_;
    MapType map_;
    void add (std::string n, Counter &c) {
        ScopedLock<Mutex> sl(lock_);
        if (map_.find(n) != map_.end()) throw n;
        map_[n] = &c;
    }
    Counter & get (std::string n) const {
        ScopedLock<Mutex> sl(lock_);
        MapType::const_iterator i = map_.find(n);
        if (i == map_.end()) throw n;
        return *(i->second);
    }
};

CounterRepository counterRepository;

Counter::Counter (std::string name) {
    counterRepository.add(name, *this);
}

如果你知道同一个计数器会增加一个以上的线程,那么使用CounterAtomic. 对于特定于线程的计数器,只需使用Counter.

于 2012-07-06T15:44:53.763 回答
1

使用共享内存(POSIX、System V、mmap 或任何可用的内存)。通过将原始内存块转换为数组定义,在其中放置一个固定长度的 volatile 无符号 32 位或 64 位整数数组(即您可以在平台上以原子方式递增的最大值)。请注意, volatile 不会让您获得原子性。它可以防止可能会破坏您的统计值的编译器优化。使用 gcc 的 __sync_add_and_fetch() 或较新的 C++11 atomic<> 类型等内在函数。

然后,您可以编写一个附加到同一块共享内存的小程序,并可以打印出一个或所有统计信息。这个小型统计阅读器程序和您的主程序必须共享一个公共头文件,该文件强制执行每个统计数据在数组中的位置。

这里明显的缺点是你被固定数量的计数器困住了。但就性能而言,它很难被击败。影响是程序中各个点处整数的原子增量。

于 2012-07-06T19:19:56.643 回答
1

在嵌入式系统中,一种常见的技术是为“日志”保留一块内存,并将其视为循环队列。编写一些可以读取这块内存的代码;这将有助于在运行时拍摄“快照”。

在网上搜索“调试日志”。应该打开一些你可以用来玩的来源。我去过的大多数商店通常都是自己开的。

如果您有额外的非易失性内存,您可以保留一个区域并写入该区域。如果您的系统足够大以支持文件系统,这也将包括文件。

最坏的情况,将数据写入调试(串行)端口。

对于实际的实时测量,我们通常使用连接到 GPIO 或测试点的示波器,并将脉冲输出到 GPIO/测试点。

于 2012-07-06T19:46:45.810 回答
0

这是一个很好的答案,@John Dibling!我有一个与此非常相似的系统。但是,我的“stat”线程每秒查询工作线程 10 次,它会影响工作线程的性能,因为每次“stat”线程请求数据时,都会有一个关键部分访问这些数据(计数器等)并且它表示在检索此数据时工作线程被阻塞。事实证明,在工作线程负载很重的情况下,这种 10Hz 的统计查询会影响工作线程的整体性能。

所以我切换到一个稍微不同的统计报告模型——而不是从主线程主动查询工作线程,我现在有工作线程向他们的专有统计存储库报告它们的基本统计计数器,主线程可以随时查询对工人没有直接影响。

于 2015-02-25T19:10:32.923 回答
0

看看 valgrind/callgrind。

它可以用于分析,这是我理解你正在寻找的。我认为它在运行时不起作用,但它可以在您的过程完成后生成。

于 2012-07-06T15:45:02.920 回答
0

如果您使用的是 C++11,则可以使用 std::atomic<>

#include <atomic>

class GlobalStatistics {
public:

    static GlobalStatistics &get() {
        static GlobalStatistics instance;
        return instance;
    }

    void incrTotalBytesProcessed(unsigned int incrBy) {
        totalBytesProcessed += incrBy;
    }

    long long getTotalBytesProcessed() const { return totalBytesProcessed; }


private:

    std::atomic_llong totalBytesProcessed;

    GlobalStatistics() { }
    GlobalStatistics(const GlobalStatistics &) = delete;
    void operator=(const GlobalStatistics &) = delete;
};
于 2015-11-24T06:40:02.843 回答