tools/profiler/LulCommonExt.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3
michael@0 4 // Copyright (c) 2006, 2010, 2012, 2013 Google Inc.
michael@0 5 // All rights reserved.
michael@0 6 //
michael@0 7 // Redistribution and use in source and binary forms, with or without
michael@0 8 // modification, are permitted provided that the following conditions are
michael@0 9 // met:
michael@0 10 //
michael@0 11 // * Redistributions of source code must retain the above copyright
michael@0 12 // notice, this list of conditions and the following disclaimer.
michael@0 13 // * Redistributions in binary form must reproduce the above
michael@0 14 // copyright notice, this list of conditions and the following disclaimer
michael@0 15 // in the documentation and/or other materials provided with the
michael@0 16 // distribution.
michael@0 17 // * Neither the name of Google Inc. nor the names of its
michael@0 18 // contributors may be used to endorse or promote products derived from
michael@0 19 // this software without specific prior written permission.
michael@0 20 //
michael@0 21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
michael@0 25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@0 26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@0 27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
michael@0 31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 32
michael@0 33 // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
michael@0 34
michael@0 35 // module.h: Define google_breakpad::Module. A Module holds debugging
michael@0 36 // information, and can write that information out as a Breakpad
michael@0 37 // symbol file.
michael@0 38
michael@0 39
michael@0 40 // (C) Copyright Greg Colvin and Beman Dawes 1998, 1999.
michael@0 41 // Copyright (c) 2001, 2002 Peter Dimov
michael@0 42 //
michael@0 43 // Permission to copy, use, modify, sell and distribute this software
michael@0 44 // is granted provided this copyright notice appears in all copies.
michael@0 45 // This software is provided "as is" without express or implied
michael@0 46 // warranty, and with no claim as to its suitability for any purpose.
michael@0 47 //
michael@0 48 // See http://www.boost.org/libs/smart_ptr/scoped_ptr.htm for documentation.
michael@0 49 //
michael@0 50
michael@0 51
michael@0 52 // This file is derived from the following files in
michael@0 53 // toolkit/crashreporter/google-breakpad:
michael@0 54 // src/common/unique_string.h
michael@0 55 // src/common/scoped_ptr.h
michael@0 56 // src/common/module.h
michael@0 57
michael@0 58 // External interface for the "Common" component of LUL.
michael@0 59
michael@0 60 #ifndef LulCommonExt_h
michael@0 61 #define LulCommonExt_h
michael@0 62
michael@0 63 #include <stdlib.h>
michael@0 64 #include <stdio.h>
michael@0 65 #include <stdint.h>
michael@0 66
michael@0 67 #include <string>
michael@0 68 #include <map>
michael@0 69 #include <vector>
michael@0 70 #include <cstddef> // for std::ptrdiff_t
michael@0 71
michael@0 72 #include "mozilla/Assertions.h"
michael@0 73 #include "mozilla/NullPtr.h"
michael@0 74
michael@0 75 namespace lul {
michael@0 76
michael@0 77 ////////////////////////////////////////////////////////////////
michael@0 78 // UniqueString
michael@0 79 //
michael@0 80
michael@0 81 // Abstract type
michael@0 82 class UniqueString;
michael@0 83
michael@0 84 // Unique-ify a string. |ToUniqueString| can never return nullptr.
michael@0 85 const UniqueString* ToUniqueString(std::string);
michael@0 86
michael@0 87 // Get the contained C string (debugging only)
michael@0 88 const char* const FromUniqueString(const UniqueString*);
michael@0 89
michael@0 90 // Some handy pre-uniqified strings. Z is an escape character:
michael@0 91 // ZS '$'
michael@0 92 // ZD '.'
michael@0 93 // Zeq '='
michael@0 94 // Zplus '+'
michael@0 95 // Zstar '*'
michael@0 96 // Zslash '/'
michael@0 97 // Zpercent '%'
michael@0 98 // Zat '@'
michael@0 99 // Zcaret '^'
michael@0 100
michael@0 101 // Note that ustr__empty and (UniqueString*)nullptr are considered
michael@0 102 // to be different.
michael@0 103 //
michael@0 104 // Unfortunately these have to be written as functions so as to
michael@0 105 // make them safe to use in static initialisers.
michael@0 106
michael@0 107 // ""
michael@0 108 inline static const UniqueString* ustr__empty() {
michael@0 109 static const UniqueString* us = nullptr;
michael@0 110 if (!us) us = ToUniqueString("");
michael@0 111 return us;
michael@0 112 }
michael@0 113
michael@0 114 // ".cfa"
michael@0 115 inline static const UniqueString* ustr__ZDcfa() {
michael@0 116 static const UniqueString* us = nullptr;
michael@0 117 if (!us) us = ToUniqueString(".cfa");
michael@0 118 return us;
michael@0 119 }
michael@0 120
michael@0 121 // ".ra"
michael@0 122 inline static const UniqueString* ustr__ZDra() {
michael@0 123 static const UniqueString* us = nullptr;
michael@0 124 if (!us) us = ToUniqueString(".ra");
michael@0 125 return us;
michael@0 126 }
michael@0 127
michael@0 128
michael@0 129 ////////////////////////////////////////////////////////////////
michael@0 130 // GUID
michael@0 131 //
michael@0 132
michael@0 133 typedef struct {
michael@0 134 uint32_t data1;
michael@0 135 uint16_t data2;
michael@0 136 uint16_t data3;
michael@0 137 uint8_t data4[8];
michael@0 138 } MDGUID; // GUID
michael@0 139
michael@0 140 typedef MDGUID GUID;
michael@0 141
michael@0 142
michael@0 143 ////////////////////////////////////////////////////////////////
michael@0 144 // scoped_ptr
michael@0 145 //
michael@0 146
michael@0 147 // scoped_ptr mimics a built-in pointer except that it guarantees deletion
michael@0 148 // of the object pointed to, either on destruction of the scoped_ptr or via
michael@0 149 // an explicit reset(). scoped_ptr is a simple solution for simple needs;
michael@0 150 // use shared_ptr or std::auto_ptr if your needs are more complex.
michael@0 151
michael@0 152 // *** NOTE ***
michael@0 153 // If your scoped_ptr is a class member of class FOO pointing to a
michael@0 154 // forward declared type BAR (as shown below), then you MUST use a non-inlined
michael@0 155 // version of the destructor. The destructor of a scoped_ptr (called from
michael@0 156 // FOO's destructor) must have a complete definition of BAR in order to
michael@0 157 // destroy it. Example:
michael@0 158 //
michael@0 159 // -- foo.h --
michael@0 160 // class BAR;
michael@0 161 //
michael@0 162 // class FOO {
michael@0 163 // public:
michael@0 164 // FOO();
michael@0 165 // ~FOO(); // Required for sources that instantiate class FOO to compile!
michael@0 166 //
michael@0 167 // private:
michael@0 168 // scoped_ptr<BAR> bar_;
michael@0 169 // };
michael@0 170 //
michael@0 171 // -- foo.cc --
michael@0 172 // #include "foo.h"
michael@0 173 // FOO::~FOO() {} // Empty, but must be non-inlined to FOO's class definition.
michael@0 174
michael@0 175 // scoped_ptr_malloc added by Google
michael@0 176 // When one of these goes out of scope, instead of doing a delete or
michael@0 177 // delete[], it calls free(). scoped_ptr_malloc<char> is likely to see
michael@0 178 // much more use than any other specializations.
michael@0 179
michael@0 180 // release() added by Google
michael@0 181 // Use this to conditionally transfer ownership of a heap-allocated object
michael@0 182 // to the caller, usually on method success.
michael@0 183
michael@0 184 template <typename T>
michael@0 185 class scoped_ptr {
michael@0 186 private:
michael@0 187
michael@0 188 T* ptr;
michael@0 189
michael@0 190 scoped_ptr(scoped_ptr const &);
michael@0 191 scoped_ptr & operator=(scoped_ptr const &);
michael@0 192
michael@0 193 public:
michael@0 194
michael@0 195 typedef T element_type;
michael@0 196
michael@0 197 explicit scoped_ptr(T* p = 0): ptr(p) {}
michael@0 198
michael@0 199 ~scoped_ptr() {
michael@0 200 delete ptr;
michael@0 201 }
michael@0 202
michael@0 203 void reset(T* p = 0) {
michael@0 204 if (ptr != p) {
michael@0 205 delete ptr;
michael@0 206 ptr = p;
michael@0 207 }
michael@0 208 }
michael@0 209
michael@0 210 T& operator*() const {
michael@0 211 MOZ_ASSERT(ptr != 0);
michael@0 212 return *ptr;
michael@0 213 }
michael@0 214
michael@0 215 T* operator->() const {
michael@0 216 MOZ_ASSERT(ptr != 0);
michael@0 217 return ptr;
michael@0 218 }
michael@0 219
michael@0 220 bool operator==(T* p) const {
michael@0 221 return ptr == p;
michael@0 222 }
michael@0 223
michael@0 224 bool operator!=(T* p) const {
michael@0 225 return ptr != p;
michael@0 226 }
michael@0 227
michael@0 228 T* get() const {
michael@0 229 return ptr;
michael@0 230 }
michael@0 231
michael@0 232 void swap(scoped_ptr & b) {
michael@0 233 T* tmp = b.ptr;
michael@0 234 b.ptr = ptr;
michael@0 235 ptr = tmp;
michael@0 236 }
michael@0 237
michael@0 238 T* release() {
michael@0 239 T* tmp = ptr;
michael@0 240 ptr = 0;
michael@0 241 return tmp;
michael@0 242 }
michael@0 243
michael@0 244 private:
michael@0 245
michael@0 246 // no reason to use these: each scoped_ptr should have its own object
michael@0 247 template <typename U> bool operator==(scoped_ptr<U> const& p) const;
michael@0 248 template <typename U> bool operator!=(scoped_ptr<U> const& p) const;
michael@0 249 };
michael@0 250
michael@0 251 template<typename T> inline
michael@0 252 void swap(scoped_ptr<T>& a, scoped_ptr<T>& b) {
michael@0 253 a.swap(b);
michael@0 254 }
michael@0 255
michael@0 256 template<typename T> inline
michael@0 257 bool operator==(T* p, const scoped_ptr<T>& b) {
michael@0 258 return p == b.get();
michael@0 259 }
michael@0 260
michael@0 261 template<typename T> inline
michael@0 262 bool operator!=(T* p, const scoped_ptr<T>& b) {
michael@0 263 return p != b.get();
michael@0 264 }
michael@0 265
michael@0 266 // scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to
michael@0 267 // is guaranteed, either on destruction of the scoped_array or via an explicit
michael@0 268 // reset(). Use shared_array or std::vector if your needs are more complex.
michael@0 269
michael@0 270 template<typename T>
michael@0 271 class scoped_array {
michael@0 272 private:
michael@0 273
michael@0 274 T* ptr;
michael@0 275
michael@0 276 scoped_array(scoped_array const &);
michael@0 277 scoped_array & operator=(scoped_array const &);
michael@0 278
michael@0 279 public:
michael@0 280
michael@0 281 typedef T element_type;
michael@0 282
michael@0 283 explicit scoped_array(T* p = 0) : ptr(p) {}
michael@0 284
michael@0 285 ~scoped_array() {
michael@0 286 delete[] ptr;
michael@0 287 }
michael@0 288
michael@0 289 void reset(T* p = 0) {
michael@0 290 if (ptr != p) {
michael@0 291 delete [] ptr;
michael@0 292 ptr = p;
michael@0 293 }
michael@0 294 }
michael@0 295
michael@0 296 T& operator[](std::ptrdiff_t i) const {
michael@0 297 MOZ_ASSERT(ptr != 0);
michael@0 298 MOZ_ASSERT(i >= 0);
michael@0 299 return ptr[i];
michael@0 300 }
michael@0 301
michael@0 302 bool operator==(T* p) const {
michael@0 303 return ptr == p;
michael@0 304 }
michael@0 305
michael@0 306 bool operator!=(T* p) const {
michael@0 307 return ptr != p;
michael@0 308 }
michael@0 309
michael@0 310 T* get() const {
michael@0 311 return ptr;
michael@0 312 }
michael@0 313
michael@0 314 void swap(scoped_array & b) {
michael@0 315 T* tmp = b.ptr;
michael@0 316 b.ptr = ptr;
michael@0 317 ptr = tmp;
michael@0 318 }
michael@0 319
michael@0 320 T* release() {
michael@0 321 T* tmp = ptr;
michael@0 322 ptr = 0;
michael@0 323 return tmp;
michael@0 324 }
michael@0 325
michael@0 326 private:
michael@0 327
michael@0 328 // no reason to use these: each scoped_array should have its own object
michael@0 329 template <typename U> bool operator==(scoped_array<U> const& p) const;
michael@0 330 template <typename U> bool operator!=(scoped_array<U> const& p) const;
michael@0 331 };
michael@0 332
michael@0 333 template<class T> inline
michael@0 334 void swap(scoped_array<T>& a, scoped_array<T>& b) {
michael@0 335 a.swap(b);
michael@0 336 }
michael@0 337
michael@0 338 template<typename T> inline
michael@0 339 bool operator==(T* p, const scoped_array<T>& b) {
michael@0 340 return p == b.get();
michael@0 341 }
michael@0 342
michael@0 343 template<typename T> inline
michael@0 344 bool operator!=(T* p, const scoped_array<T>& b) {
michael@0 345 return p != b.get();
michael@0 346 }
michael@0 347
michael@0 348
michael@0 349 // This class wraps the c library function free() in a class that can be
michael@0 350 // passed as a template argument to scoped_ptr_malloc below.
michael@0 351 class ScopedPtrMallocFree {
michael@0 352 public:
michael@0 353 inline void operator()(void* x) const {
michael@0 354 free(x);
michael@0 355 }
michael@0 356 };
michael@0 357
michael@0 358 // scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
michael@0 359 // second template argument, the functor used to free the object.
michael@0 360
michael@0 361 template<typename T, typename FreeProc = ScopedPtrMallocFree>
michael@0 362 class scoped_ptr_malloc {
michael@0 363 private:
michael@0 364
michael@0 365 T* ptr;
michael@0 366
michael@0 367 scoped_ptr_malloc(scoped_ptr_malloc const &);
michael@0 368 scoped_ptr_malloc & operator=(scoped_ptr_malloc const &);
michael@0 369
michael@0 370 public:
michael@0 371
michael@0 372 typedef T element_type;
michael@0 373
michael@0 374 explicit scoped_ptr_malloc(T* p = 0): ptr(p) {}
michael@0 375
michael@0 376 ~scoped_ptr_malloc() {
michael@0 377 free_((void*) ptr);
michael@0 378 }
michael@0 379
michael@0 380 void reset(T* p = 0) {
michael@0 381 if (ptr != p) {
michael@0 382 free_((void*) ptr);
michael@0 383 ptr = p;
michael@0 384 }
michael@0 385 }
michael@0 386
michael@0 387 T& operator*() const {
michael@0 388 MOZ_ASSERT(ptr != 0);
michael@0 389 return *ptr;
michael@0 390 }
michael@0 391
michael@0 392 T* operator->() const {
michael@0 393 MOZ_ASSERT(ptr != 0);
michael@0 394 return ptr;
michael@0 395 }
michael@0 396
michael@0 397 bool operator==(T* p) const {
michael@0 398 return ptr == p;
michael@0 399 }
michael@0 400
michael@0 401 bool operator!=(T* p) const {
michael@0 402 return ptr != p;
michael@0 403 }
michael@0 404
michael@0 405 T* get() const {
michael@0 406 return ptr;
michael@0 407 }
michael@0 408
michael@0 409 void swap(scoped_ptr_malloc & b) {
michael@0 410 T* tmp = b.ptr;
michael@0 411 b.ptr = ptr;
michael@0 412 ptr = tmp;
michael@0 413 }
michael@0 414
michael@0 415 T* release() {
michael@0 416 T* tmp = ptr;
michael@0 417 ptr = 0;
michael@0 418 return tmp;
michael@0 419 }
michael@0 420
michael@0 421 private:
michael@0 422
michael@0 423 // no reason to use these: each scoped_ptr_malloc should have its own object
michael@0 424 template <typename U, typename GP>
michael@0 425 bool operator==(scoped_ptr_malloc<U, GP> const& p) const;
michael@0 426 template <typename U, typename GP>
michael@0 427 bool operator!=(scoped_ptr_malloc<U, GP> const& p) const;
michael@0 428
michael@0 429 static FreeProc const free_;
michael@0 430 };
michael@0 431
michael@0 432 template<typename T, typename FP>
michael@0 433 FP const scoped_ptr_malloc<T,FP>::free_ = FP();
michael@0 434
michael@0 435 template<typename T, typename FP> inline
michael@0 436 void swap(scoped_ptr_malloc<T,FP>& a, scoped_ptr_malloc<T,FP>& b) {
michael@0 437 a.swap(b);
michael@0 438 }
michael@0 439
michael@0 440 template<typename T, typename FP> inline
michael@0 441 bool operator==(T* p, const scoped_ptr_malloc<T,FP>& b) {
michael@0 442 return p == b.get();
michael@0 443 }
michael@0 444
michael@0 445 template<typename T, typename FP> inline
michael@0 446 bool operator!=(T* p, const scoped_ptr_malloc<T,FP>& b) {
michael@0 447 return p != b.get();
michael@0 448 }
michael@0 449
michael@0 450
michael@0 451 ////////////////////////////////////////////////////////////////
michael@0 452 // Module
michael@0 453 //
michael@0 454
michael@0 455 // A Module represents the contents of a module, and supports methods
michael@0 456 // for adding information produced by parsing STABS or DWARF data
michael@0 457 // --- possibly both from the same file --- and then writing out the
michael@0 458 // unified contents as a Breakpad-format symbol file.
michael@0 459 class Module {
michael@0 460 public:
michael@0 461 // The type of addresses and sizes in a symbol table.
michael@0 462 typedef uint64_t Address;
michael@0 463
michael@0 464 // Representation of an expression. This can either be a postfix
michael@0 465 // expression, in which case it is stored as a string, or a simple
michael@0 466 // expression of the form (identifier + imm) or *(identifier + imm).
michael@0 467 // It can also be invalid (denoting "no value").
michael@0 468 enum ExprHow {
michael@0 469 kExprInvalid = 1,
michael@0 470 kExprPostfix,
michael@0 471 kExprSimple,
michael@0 472 kExprSimpleMem
michael@0 473 };
michael@0 474
michael@0 475 struct Expr {
michael@0 476 // Construct a simple-form expression
michael@0 477 Expr(const UniqueString* ident, long offset, bool deref) {
michael@0 478 if (ident == ustr__empty()) {
michael@0 479 Expr();
michael@0 480 } else {
michael@0 481 postfix_ = "";
michael@0 482 ident_ = ident;
michael@0 483 offset_ = offset;
michael@0 484 how_ = deref ? kExprSimpleMem : kExprSimple;
michael@0 485 }
michael@0 486 }
michael@0 487
michael@0 488 // Construct an invalid expression
michael@0 489 Expr() {
michael@0 490 postfix_ = "";
michael@0 491 ident_ = nullptr;
michael@0 492 offset_ = 0;
michael@0 493 how_ = kExprInvalid;
michael@0 494 }
michael@0 495
michael@0 496 // Return the postfix expression string, either directly,
michael@0 497 // if this is a postfix expression, or by synthesising it
michael@0 498 // for a simple expression.
michael@0 499 std::string getExprPostfix() const {
michael@0 500 switch (how_) {
michael@0 501 case kExprPostfix:
michael@0 502 return postfix_;
michael@0 503 case kExprSimple:
michael@0 504 case kExprSimpleMem: {
michael@0 505 char buf[40];
michael@0 506 sprintf(buf, " %ld %c%s", labs(offset_), offset_ < 0 ? '-' : '+',
michael@0 507 how_ == kExprSimple ? "" : " ^");
michael@0 508 return std::string(FromUniqueString(ident_)) + std::string(buf);
michael@0 509 }
michael@0 510 case kExprInvalid:
michael@0 511 default:
michael@0 512 MOZ_ASSERT(0 && "getExprPostfix: invalid Module::Expr type");
michael@0 513 return "Expr::genExprPostfix: kExprInvalid";
michael@0 514 }
michael@0 515 }
michael@0 516
michael@0 517 // The identifier that gives the starting value for simple expressions.
michael@0 518 const UniqueString* ident_;
michael@0 519 // The offset to add for simple expressions.
michael@0 520 long offset_;
michael@0 521 // The Postfix expression string to evaluate for non-simple expressions.
michael@0 522 std::string postfix_;
michael@0 523 // The operation expressed by this expression.
michael@0 524 ExprHow how_;
michael@0 525 };
michael@0 526
michael@0 527 // A map from register names to expressions that recover
michael@0 528 // their values. This can represent a complete set of rules to
michael@0 529 // follow at some address, or a set of changes to be applied to an
michael@0 530 // extant set of rules.
michael@0 531 // NOTE! there are two completely different types called RuleMap. This
michael@0 532 // is one of them.
michael@0 533 typedef std::map<const UniqueString*, Expr> RuleMap;
michael@0 534
michael@0 535 // A map from addresses to RuleMaps, representing changes that take
michael@0 536 // effect at given addresses.
michael@0 537 typedef std::map<Address, RuleMap> RuleChangeMap;
michael@0 538
michael@0 539 // A range of 'STACK CFI' stack walking information. An instance of
michael@0 540 // this structure corresponds to a 'STACK CFI INIT' record and the
michael@0 541 // subsequent 'STACK CFI' records that fall within its range.
michael@0 542 struct StackFrameEntry {
michael@0 543 // The starting address and number of bytes of machine code this
michael@0 544 // entry covers.
michael@0 545 Address address, size;
michael@0 546
michael@0 547 // The initial register recovery rules, in force at the starting
michael@0 548 // address.
michael@0 549 RuleMap initial_rules;
michael@0 550
michael@0 551 // A map from addresses to rule changes. To find the rules in
michael@0 552 // force at a given address, start with initial_rules, and then
michael@0 553 // apply the changes given in this map for all addresses up to and
michael@0 554 // including the address you're interested in.
michael@0 555 RuleChangeMap rule_changes;
michael@0 556 };
michael@0 557
michael@0 558 // Create a new module with the given name, operating system,
michael@0 559 // architecture, and ID string.
michael@0 560 Module(const std::string &name, const std::string &os,
michael@0 561 const std::string &architecture, const std::string &id);
michael@0 562 ~Module();
michael@0 563
michael@0 564 private:
michael@0 565
michael@0 566 // Module header entries.
michael@0 567 std::string name_, os_, architecture_, id_;
michael@0 568 };
michael@0 569
michael@0 570
michael@0 571 } // namespace lul
michael@0 572
michael@0 573 #endif // LulCommonExt_h

mercurial