aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/lwtrace/lwprobe.h
blob: ca6e4eaf39528060ffdd8b6d827ef584aa299b9b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#pragma once

#include "control.h"
#include "probe.h"

#include <ctype.h>

namespace NLWTrace {
#ifndef LWTRACE_DISABLE

    // Box that holds dynamically created probe
    // NOTE: must be allocated on heap
    template <LWTRACE_TEMPLATE_PARAMS>
    class TLWProbe: public IBox, public TUserProbe<LWTRACE_TEMPLATE_ARGS> {
    private:
        // Storage for strings referenced by TEvent
        TString Name;
        TString Provider;
        TVector<TString> Groups;
        TVector<TString> Params;
        TVector<TProbeRegistry*> Registries; // Note that we assume that registry lives longer than probe

    public:
        TLWProbe(const TString& provider, const TString& name, const TVector<TString>& groups, const TVector<TString>& params)
            : IBox(true)
            , Name(name)
            , Provider(provider)
            , Groups(groups)
            , Params(params)
        {
            // initialize TProbe
            TProbe& probe = this->Probe;
            probe.Init();

            // initialize TEvent
            Y_ABORT_UNLESS(IsCppIdentifier(Name), "probe '%s' is not C++ identifier", Name.data());
            Y_ABORT_UNLESS(IsCppIdentifier(Provider), "provider '%s' is not C++ identifier in probe %s", Provider.data(), Name.data());
            probe.Event.Name = Name.c_str();
            Zero(probe.Event.Groups);
            probe.Event.Groups[0] = Provider.c_str();
            auto i = Groups.begin(), ie = Groups.end();
            Y_ABORT_UNLESS(Groups.size() < LWTRACE_MAX_GROUPS, "too many groups in probe %s", Name.data());
            for (size_t n = 1; n < LWTRACE_MAX_GROUPS && i != ie; n++, ++i) {
                Y_ABORT_UNLESS(IsCppIdentifier(*i), "group '%s' is not C++ identifier in probe %s", i->data(), Name.data());
                probe.Event.Groups[n] = i->c_str();
            }

            // initialize TSignature
            using TUsrSign = TUserSignature<LWTRACE_TEMPLATE_ARGS>;
            Y_ABORT_UNLESS(TUsrSign::ParamCount == (int)Params.size(), "param count mismatch in probe %s: %d != %d",
                     Name.data(), int(Params.size()), TUsrSign::ParamCount);
            TSignature& signature = probe.Event.Signature;
            signature.ParamTypes = TUsrSign::ParamTypes;
            Zero(signature.ParamNames);
            auto j = Params.begin(), je = Params.end();
            for (size_t n = 0; n < LWTRACE_MAX_PARAMS && j != je; n++, ++j) {
                Y_ABORT_UNLESS(IsCppIdentifier(*j), "param '%s' is not C++ identifier in probe %s", j->data(), Name.data());
                signature.ParamNames[n] = j->c_str();
            }
            signature.ParamCount = TUsrSign::ParamCount;
            signature.SerializeParamsFunc = &TUsrSign::SerializeParams;
            signature.CloneParamsFunc = &TUsrSign::CloneParams;
            signature.DestroyParamsFunc = &TUsrSign::DestroyParams;
            signature.SerializeToPbFunc = &TUsrSign::SerializeToPb;
            signature.DeserializeFromPbFunc = &TUsrSign::DeserializeFromPb;

            // register probe in global registry
            Register(*Singleton<NLWTrace::TProbeRegistry>());
        }

        ~TLWProbe() {
            Unregister();
        }

        void Register(TProbeRegistry& reg) {
            Registries.push_back(&reg);
            reg.AddProbe(TBoxPtr(this)); // NOTE: implied `this' object is created with new operator
        }

        void Unregister() {
            // TODO[serxa]: make sure registry never dies before probe it contain
            // TODO[serxa]: make sure probe never dies before TSession that uses it
            for (TProbeRegistry* reg : Registries) {
                reg->RemoveProbe(&this->Probe);
            }
        }

        TProbe* GetProbe() override {
            return &this->Probe;
        }

    private:
        static bool IsCppIdentifier(const TString& str) {
            bool first = true;
            for (char c : str) {
                if (first) {
                    first = false;
                    if (!(isalpha(c) || c == '_')) {
                        return false;
                    }
                } else if (!(isalnum(c) || c == '_')) {
                    return false;
                }
            }
            return true;
        }
    };

#endif
}