Verilator

Форк
0
/
verilated_cov.cpp 
529 строк · 20.9 Кб
1
// -*- mode: C++; c-file-style: "cc-mode" -*-
2
//=============================================================================
3
//
4
// Code available from: https://verilator.org
5
//
6
// Copyright 2001-2024 by Wilson Snyder. This program is free software; you
7
// can redistribute it and/or modify it under the terms of either the GNU
8
// Lesser General Public License Version 3 or the Perl Artistic License
9
// Version 2.0.
10
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
11
//
12
//=============================================================================
13
///
14
/// \file
15
/// \brief Verilated coverage analysis implementation code
16
///
17
/// This file must be compiled and linked against all Verilated objects
18
/// that use coverage.
19
///
20
/// Use "verilator --coverage" to add this to the Makefile for the linker.
21
///
22
//=============================================================================
23

24
#include "verilatedos.h"
25

26
#include "verilated_cov.h"
27

28
#include "verilated.h"
29
#include "verilated_cov_key.h"
30

31
#include <deque>
32
#include <fstream>
33
#include <map>
34
#include <utility>
35

36
//=============================================================================
37
// VerilatedCovConst
38
// Implementation constants
39

40
struct VerilatedCovConst VL_NOT_FINAL {
41
    // TYPES
42
    enum { MAX_KEYS = 33 };  // Maximum user arguments + filename+lineno
43
    enum { KEY_UNDEF = 0 };  // Magic key # for unspecified values
44
};
45

46
//=============================================================================
47
// VerilatedCovImpItem
48
// Implementation class for a VerilatedCov item
49

50
class VerilatedCovImpItem VL_NOT_FINAL {
51
public:  // But only local to this file
52
    // MEMBERS
53
    int m_keys[VerilatedCovConst::MAX_KEYS];  // Key
54
    int m_vals[VerilatedCovConst::MAX_KEYS];  // Value for specified key
55
    // CONSTRUCTORS
56
    // Derived classes should call zero() in their constructor
57
    VerilatedCovImpItem() {
58
        for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
59
            m_keys[i] = VerilatedCovConst::KEY_UNDEF;
60
            m_vals[i] = 0;
61
        }
62
    }
63
    virtual ~VerilatedCovImpItem() = default;
64
    virtual uint64_t count() const = 0;
65
    virtual void zero() const = 0;
66
};
67

68
//=============================================================================
69
// VerilatedCoverItem templated for a specific class
70
// Creates a new coverage item for the specified type.
71
// This isn't in the header file for auto-magic conversion because it
72
// inlines to too much code and makes compilation too slow.
73

74
template <class T>
75
class VerilatedCoverItemSpec final : public VerilatedCovImpItem {
76
private:
77
    // MEMBERS
78
    T* m_countp;  // Count value
79
public:
80
    // METHODS
81
    // cppcheck-suppress truncLongCastReturn
82
    uint64_t count() const override { return *m_countp; }
83
    void zero() const override { *m_countp = 0; }
84
    // CONSTRUCTORS
85
    // cppcheck-suppress noExplicitConstructor
86
    explicit VerilatedCoverItemSpec(T* countp)
87
        : m_countp{countp} {
88
        *m_countp = 0;
89
    }
90
    ~VerilatedCoverItemSpec() override = default;
91
};
92

93
//=============================================================================
94
// VerilatedCovImp
95
//
96
// Implementation class for VerilatedCovContext.  See that class for
97
// public method information.  All value and keys are indexed into a
98
// unique number.  Thus we can greatly reduce the storage requirements for
99
// otherwise identical keys.
100

101
class VerilatedCovImp final : public VerilatedCovContext {
102
private:
103
    // TYPES
104
    using ValueIndexMap = std::map<const std::string, int>;
105
    using IndexValueMap = std::map<int, std::string>;
106
    using ItemList = std::deque<VerilatedCovImpItem*>;
107

108
    // MEMBERS
109
    VerilatedContext* const m_contextp;  // Context VerilatedCovImp is pointed-to by
110
    mutable VerilatedMutex m_mutex;  // Protects all members
111
    ValueIndexMap m_valueIndexes VL_GUARDED_BY(m_mutex);  // Unique arbitrary value for values
112
    IndexValueMap m_indexValues VL_GUARDED_BY(m_mutex);  // Unique arbitrary value for keys
113
    ItemList m_items VL_GUARDED_BY(m_mutex);  // List of all items
114
    int m_nextIndex VL_GUARDED_BY(m_mutex)
115
        = (VerilatedCovConst::KEY_UNDEF + 1);  // Next insert value
116

117
    VerilatedCovImpItem* m_insertp VL_GUARDED_BY(m_mutex) = nullptr;  // Item about to insert
118
    const char* m_insertFilenamep VL_GUARDED_BY(m_mutex) = nullptr;  // Filename about to insert
119
    int m_insertLineno VL_GUARDED_BY(m_mutex) = 0;  // Line number about to insert
120
    bool m_forcePerInstance VL_GUARDED_BY(m_mutex) = false;  // Force per_instance
121

122
public:
123
    // CONSTRUCTORS
124
    explicit VerilatedCovImp(VerilatedContext* contextp)
125
        : m_contextp{contextp} {}
126
    VL_UNCOPYABLE(VerilatedCovImp);
127

128
protected:
129
    friend class VerilatedCovContext;
130
    ~VerilatedCovImp() override { clearGuts(); }
131

132
private:
133
    // PRIVATE METHODS
134
    int valueIndex(const std::string& value) VL_REQUIRES(m_mutex) {
135
        const auto iter = m_valueIndexes.find(value);
136
        if (iter != m_valueIndexes.end()) return iter->second;
137
        ++m_nextIndex;
138
        assert(m_nextIndex > 0);  // Didn't rollover
139
        m_valueIndexes.emplace(value, m_nextIndex);
140
        m_indexValues.emplace(m_nextIndex, value);
141
        return m_nextIndex;
142
    }
143
    static std::string dequote(const std::string& text) VL_PURE {
144
        // Quote any special characters
145
        std::string rtn;
146
        for (const char* pos = text.c_str(); *pos; ++pos) {
147
            if (!std::isprint(*pos) || *pos == '%' || *pos == '"') {
148
                constexpr size_t LEN_MAX_HEX = 20;
149
                char hex[LEN_MAX_HEX];
150
                VL_SNPRINTF(hex, LEN_MAX_HEX, "%%%02X", pos[0]);
151
                rtn += hex;
152
            } else {
153
                rtn += *pos;
154
            }
155
        }
156
        return rtn;
157
    }
158
    static bool legalKey(const std::string& key) VL_PURE {
159
        // Because we compress long keys to a single letter, and
160
        // don't want applications to either get confused if they use
161
        // a letter differently, nor want them to rely on our compression...
162
        // (Considered using numeric keys, but will remain back compatible.)
163
        if (key.length() < 2) return false;
164
        if (key.length() == 2 && std::isdigit(key[1])) return false;
165
        return true;
166
    }
167
    static std::string keyValueFormatter(const std::string& key,
168
                                         const std::string& value) VL_PURE {
169
        std::string name;
170
        if (key.length() == 1 && std::isalpha(key[0])) {
171
            name += "\001"s + key;
172
        } else {
173
            name += "\001"s + dequote(key);
174
        }
175
        name += "\002"s + dequote(value);
176
        return name;
177
    }
178
    static std::string combineHier(const std::string& old, const std::string& add) VL_PURE {
179
        // (foo.a.x, foo.b.x) => foo.*.x
180
        // (foo.a.x, foo.b.y) => foo.*
181
        // (foo.a.x, foo.b)   => foo.*
182
        if (old == add) return add;
183
        if (old.empty()) return add;
184
        if (add.empty()) return old;
185

186
        const char* const a = old.c_str();
187
        const char* const b = add.c_str();
188

189
        // Scan forward to first mismatch
190
        const char* apre = a;
191
        const char* bpre = b;
192
        while (*apre == *bpre) {
193
            ++apre;
194
            ++bpre;
195
        }
196

197
        // We used to backup and split on only .'s but it seems better to be verbose
198
        // and not assume . is the separator
199
        const size_t prefix_len = apre - a;
200
        const std::string prefix = std::string{a, prefix_len};
201

202
        // Scan backward to last mismatch
203
        const char* apost = a + std::strlen(a) - 1;
204
        const char* bpost = b + std::strlen(b) - 1;
205
        while (*apost == *bpost && apost > apre && bpost > bpre) {
206
            --apost;
207
            --bpost;
208
        }
209

210
        // Forward to . so we have a whole word
211
        const std::string suffix = *bpost ? std::string{bpost + 1} : "";
212

213
        std::string result = prefix + "*" + suffix;
214

215
        // std::cout << "\nch pre=" << prefix << "  s=" << suffix << "\nch a="
216
        // << old << "\nch b=" << add << "\ncho=" << result << "\n";
217
        return result;
218
    }
219
    bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match)
220
        VL_REQUIRES(m_mutex) {
221
        for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
222
            if (itemp->m_keys[i] != VerilatedCovConst::KEY_UNDEF) {
223
                // We don't compare keys, only values
224
                const std::string val = m_indexValues[itemp->m_vals[i]];
225
                if (std::string::npos != val.find(match)) {  // Found
226
                    return true;
227
                }
228
            }
229
        }
230
        return false;
231
    }
232
    static void selftest() VL_MT_SAFE {
233
        // Little selftest
234
#define SELF_CHECK(got, exp) \
235
    do { \
236
        if ((got) != (exp)) VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: selftest"); \
237
    } while (0)
238
        SELF_CHECK(combineHier("a.b.c", "a.b.c"), "a.b.c");
239
        SELF_CHECK(combineHier("a.b.c", "a.b"), "a.b*");
240
        SELF_CHECK(combineHier("a.x.c", "a.y.c"), "a.*.c");
241
        SELF_CHECK(combineHier("a.z.z.z.c", "a.b.c"), "a.*.c");
242
        SELF_CHECK(combineHier("z", "a"), "*");
243
        SELF_CHECK(combineHier("q.a", "q.b"), "q.*");
244
        SELF_CHECK(combineHier("q.za", "q.zb"), "q.z*");
245
        SELF_CHECK(combineHier("1.2.3.a", "9.8.7.a"), "*.a");
246
#undef SELF_CHECK
247
    }
248
    void clearGuts() VL_REQUIRES(m_mutex) {
249
        for (const auto& itemp : m_items) VL_DO_DANGLING(delete itemp, itemp);
250
        m_items.clear();
251
        m_indexValues.clear();
252
        m_valueIndexes.clear();
253
        m_nextIndex = VerilatedCovConst::KEY_UNDEF + 1;
254
    }
255

256
public:
257
    // PUBLIC METHODS
258
    std::string defaultFilename() VL_MT_SAFE { return m_contextp->coverageFilename(); }
259
    void forcePerInstance(const bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) {
260
        Verilated::quiesce();
261
        const VerilatedLockGuard lock{m_mutex};
262
        m_forcePerInstance = flag;
263
    }
264
    void clear() VL_MT_SAFE_EXCLUDES(m_mutex) {
265
        Verilated::quiesce();
266
        const VerilatedLockGuard lock{m_mutex};
267
        clearGuts();
268
    }
269
    void clearNonMatch(const char* const matchp) VL_MT_SAFE_EXCLUDES(m_mutex) {
270
        Verilated::quiesce();
271
        const VerilatedLockGuard lock{m_mutex};
272
        if (matchp && matchp[0]) {
273
            ItemList newlist;
274
            for (const auto& itemp : m_items) {
275
                if (!itemMatchesString(itemp, matchp)) {
276
                    VL_DO_DANGLING(delete itemp, itemp);
277
                } else {
278
                    newlist.push_back(itemp);
279
                }
280
            }
281
            m_items = newlist;
282
        }
283
    }
284
    void zero() VL_MT_SAFE_EXCLUDES(m_mutex) {
285
        Verilated::quiesce();
286
        const VerilatedLockGuard lock{m_mutex};
287
        for (const auto& itemp : m_items) itemp->zero();
288
    }
289

290
    // We assume there's always call to i/f/p in that order
291
    void inserti(VerilatedCovImpItem* itemp) VL_MT_SAFE_EXCLUDES(m_mutex) {
292
        const VerilatedLockGuard lock{m_mutex};
293
        assert(!m_insertp);
294
        m_insertp = itemp;
295
    }
296
    void insertf(const char* const filenamep, const int lineno) VL_MT_SAFE_EXCLUDES(m_mutex) {
297
        const VerilatedLockGuard lock{m_mutex};
298
        m_insertFilenamep = filenamep;
299
        m_insertLineno = lineno;
300
    }
301
    void insertp(const char* ckeyps[VerilatedCovConst::MAX_KEYS],
302
                 const char* valps[VerilatedCovConst::MAX_KEYS]) VL_MT_SAFE_EXCLUDES(m_mutex) {
303
        const VerilatedLockGuard lock{m_mutex};
304
        assert(m_insertp);
305
        // First two key/vals are filename
306
        ckeyps[0] = "filename";
307
        valps[0] = m_insertFilenamep;
308
        const std::string linestr = std::to_string(m_insertLineno);
309
        ckeyps[1] = "lineno";
310
        valps[1] = linestr.c_str();
311
        // Default page if not specified
312
        const char* fnstartp = m_insertFilenamep;
313
        while (const char* foundp = std::strchr(fnstartp, '/')) fnstartp = foundp + 1;
314
        const char* fnendp = fnstartp;
315
        for (; *fnendp && *fnendp != '.'; fnendp++) {}
316
        const size_t page_len = fnendp - fnstartp;
317
        const std::string page_default = "sp_user/" + std::string{fnstartp, page_len};
318
        ckeyps[2] = "page";
319
        valps[2] = page_default.c_str();
320

321
        // Keys -> strings
322
        std::array<std::string, VerilatedCovConst::MAX_KEYS> keys;
323
        for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
324
            if (ckeyps[i] && ckeyps[i][0]) keys[i] = ckeyps[i];
325
        }
326
        // Ignore empty keys
327
        for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
328
            if (!keys[i].empty()) {
329
                for (int j = i + 1; j < VerilatedCovConst::MAX_KEYS; ++j) {
330
                    if (keys[i] == keys[j]) {  // Duplicate key.  Keep the last one
331
                        keys[i] = "";
332
                        break;
333
                    }
334
                }
335
            }
336
        }
337
        // Insert the values
338
        int addKeynum = 0;
339
        for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
340
            const std::string key = keys[i];
341
            if (!keys[i].empty()) {
342
                const std::string val = valps[i];
343
                // std::cout << "   " << __FUNCTION__ << "  " << key << " = " << val << "\n";
344
                m_insertp->m_keys[addKeynum] = valueIndex(key);
345
                m_insertp->m_vals[addKeynum] = valueIndex(val);
346
                ++addKeynum;
347
                if (VL_UNCOVERABLE(!legalKey(key))) {
348
                    const std::string msg
349
                        = ("%Error: Coverage keys of one character, or letter+digit are illegal: "
350
                           + key);  // LCOV_EXCL_LINE
351
                    VL_FATAL_MT("", 0, "", msg.c_str());
352
                }
353
            }
354
        }
355
        m_items.push_back(m_insertp);
356
        // Prepare for next
357
        m_insertp = nullptr;
358
    }
359

360
    void write(const std::string& filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
361
        Verilated::quiesce();
362
        const VerilatedLockGuard lock{m_mutex};
363
        selftest();
364

365
        std::ofstream os{filename};
366
        if (os.fail()) {
367
            const std::string msg = "%Error: Can't write '"s + filename + "'";
368
            VL_FATAL_MT("", 0, "", msg.c_str());
369
            return;
370
        }
371
        os << "# SystemC::Coverage-3\n";
372

373
        // Build list of events; totalize if collapsing hierarchy
374
        std::map<const std::string, std::pair<std::string, uint64_t>> eventCounts;
375
        for (const auto& itemp : m_items) {
376
            std::string name;
377
            std::string hier;
378
            bool per_instance = false;
379
            if (m_forcePerInstance) per_instance = true;
380

381
            for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
382
                if (itemp->m_keys[i] != VerilatedCovConst::KEY_UNDEF) {
383
                    const std::string key
384
                        = VerilatedCovKey::shortKey(m_indexValues[itemp->m_keys[i]]);
385
                    const std::string val = m_indexValues[itemp->m_vals[i]];
386
                    if (key == VL_CIK_PER_INSTANCE) {
387
                        if (val != "0") per_instance = true;
388
                    }
389
                    if (key == VL_CIK_HIER) {
390
                        hier = val;
391
                    } else {
392
                        // Print it
393
                        name += keyValueFormatter(key, val);
394
                    }
395
                }
396
            }
397
            if (per_instance) {  // Not collapsing hierarchies
398
                name += keyValueFormatter(VL_CIK_HIER, hier);
399
                hier = "";
400
            }
401

402
            // Group versus point labels don't matter here, downstream
403
            // deals with it.  Seems bad for sizing though and doesn't
404
            // allow easy addition of new group codes (would be
405
            // inefficient)
406

407
            // Find or insert the named event
408
            const auto cit = eventCounts.find(name);
409
            if (cit != eventCounts.end()) {
410
                const std::string& oldhier = cit->second.first;
411
                cit->second.second += itemp->count();
412
                cit->second.first = combineHier(oldhier, hier);
413
            } else {
414
                eventCounts.emplace(name, std::make_pair(hier, itemp->count()));
415
            }
416
        }
417

418
        // Output body
419
        for (const auto& i : eventCounts) {
420
            os << "C '" << std::dec;
421
            os << i.first;
422
            if (!i.second.first.empty()) os << keyValueFormatter(VL_CIK_HIER, i.second.first);
423
            os << "' " << i.second.second;
424
            os << '\n';
425
        }
426
    }
427
};
428

429
//=============================================================================
430
// VerilatedCovContext
431

432
std::string VerilatedCovContext::defaultFilename() VL_MT_SAFE { return impp()->defaultFilename(); }
433
void VerilatedCovContext::forcePerInstance(bool flag) VL_MT_SAFE {
434
    impp()->forcePerInstance(flag);
435
}
436
void VerilatedCovContext::clear() VL_MT_SAFE { impp()->clear(); }
437
void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE {
438
    impp()->clearNonMatch(matchp);
439
}
440
void VerilatedCovContext::zero() VL_MT_SAFE { impp()->zero(); }
441
void VerilatedCovContext::write(const std::string& filename) VL_MT_SAFE {
442
    impp()->write(filename);
443
}
444
void VerilatedCovContext::_inserti(uint32_t* itemp) VL_MT_SAFE {
445
    impp()->inserti(new VerilatedCoverItemSpec<uint32_t>{itemp});
446
}
447
void VerilatedCovContext::_inserti(uint64_t* itemp) VL_MT_SAFE {
448
    impp()->inserti(new VerilatedCoverItemSpec<uint64_t>{itemp});
449
}
450
void VerilatedCovContext::_insertf(const char* filename, int lineno) VL_MT_SAFE {
451
    impp()->insertf(filename, lineno);
452
}
453

454
#ifndef DOXYGEN
455
#define K(n) const char* key##n
456
#define A(n) const char *key##n, const char *valp##n  // Argument list
457
#define C(n) key##n, valp##n  // Calling argument list
458
#define N(n) "", ""  // Null argument list
459
void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9),
460
                                   A(10), A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18),
461
                                   A(19), A(20), A(21), A(22), A(23), A(24), A(25), A(26), A(27),
462
                                   A(28), A(29)) VL_MT_SAFE {
463
    const char* keyps[VerilatedCovConst::MAX_KEYS]
464
        = {nullptr, nullptr, nullptr,  // filename,lineno,page
465
           key0,    key1,    key2,    key3,  key4,  key5,  key6,  key7,  key8,  key9,
466
           key10,   key11,   key12,   key13, key14, key15, key16, key17, key18, key19,
467
           key20,   key21,   key22,   key23, key24, key25, key26, key27, key28, key29};
468
    const char* valps[VerilatedCovConst::MAX_KEYS]
469
        = {nullptr, nullptr, nullptr,  // filename,lineno,page
470
           valp0,   valp1,   valp2,   valp3,  valp4,  valp5,  valp6,  valp7,  valp8,  valp9,
471
           valp10,  valp11,  valp12,  valp13, valp14, valp15, valp16, valp17, valp18, valp19,
472
           valp20,  valp21,  valp22,  valp23, valp24, valp25, valp26, valp27, valp28, valp29};
473
    impp()->insertp(keyps, valps);
474
}
475

476
// And versions with fewer arguments  (oh for a language with named parameters!)
477
void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8),
478
                                   A(9)) VL_MT_SAFE {
479
    _insertp(C(0), C(1), C(2), C(3), C(4), C(5), C(6), C(7), C(8), C(9), N(10), N(11), N(12),
480
             N(13), N(14), N(15), N(16), N(17), N(18), N(19), N(20), N(21), N(22), N(23), N(24),
481
             N(25), N(26), N(27), N(28), N(29));
482
}
483
void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9),
484
                                   A(10), A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18),
485
                                   A(19)) VL_MT_SAFE {
486
    _insertp(C(0), C(1), C(2), C(3), C(4), C(5), C(6), C(7), C(8), C(9), C(10), C(11), C(12),
487
             C(13), C(14), C(15), C(16), C(17), C(18), C(19), N(20), N(21), N(22), N(23), N(24),
488
             N(25), N(26), N(27), N(28), N(29));
489
}
490
// Backward compatibility for Verilator
491
void VerilatedCovContext::_insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4),
492
                                   const std::string& val4, A(5), A(6), A(7)) VL_MT_SAFE {
493
    const std::string val2str = std::to_string(val2);
494
    const std::string val3str = std::to_string(val3);
495
    _insertp(C(0), C(1), key2, val2str.c_str(), key3, val3str.c_str(), key4, val4.c_str(), C(5),
496
             C(6), C(7), N(8), N(9), N(10), N(11), N(12), N(13), N(14), N(15), N(16), N(17), N(18),
497
             N(19), N(20), N(21), N(22), N(23), N(24), N(25), N(26), N(27), N(28), N(29));
498
}
499
#undef A
500
#undef C
501
#undef N
502
#undef K
503

504
#endif  // DOXYGEN
505

506
//=============================================================================
507
// VerilatedCov
508

509
#ifndef VL_NO_LEGACY
510
VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE {
511
    return Verilated::threadContextp()->coveragep();
512
}
513
#endif
514

515
//=============================================================================
516
// VerilatedContext
517

518
VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE {
519
    static VerilatedMutex s_mutex;
520
    // cppcheck-suppress identicalInnerCondition
521
    if (VL_UNLIKELY(!m_coveragep)) {
522
        const VerilatedLockGuard lock{s_mutex};
523
        // cppcheck-suppress identicalInnerCondition
524
        if (VL_LIKELY(!m_coveragep)) {  // LCOV_EXCL_LINE // Not redundant, prevents race
525
            m_coveragep.reset(new VerilatedCovImp{this});
526
        }
527
    }
528
    return reinterpret_cast<VerilatedCovContext*>(m_coveragep.get());
529
}
530

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.