├── .gitignore ├── _demo ├── demo.png └── demo.go ├── go.mod ├── .gitmodules ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE.md └── workflows │ ├── go.yml │ └── cmake.yml ├── go.sum ├── cc ├── chrometracing_init_test.cc ├── compat.cc ├── CMakeLists.txt ├── chrometracing_test.cc ├── chrometracing.h └── chrometracing.cc ├── README.md ├── CMakeLists.txt ├── docs ├── contributing.md └── code-of-conduct.md ├── traceinternal └── traceinternal.go ├── cmd └── combine │ └── combinetraces.go ├── chrometracing.py ├── combine ├── combine.go └── combine_test.go ├── chrometracing.go └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | build 3 | -------------------------------------------------------------------------------- /_demo/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/chrometracing/HEAD/_demo/demo.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/google/chrometracing 2 | 3 | go 1.15 4 | 5 | require github.com/google/go-cmp v0.5.4 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "abseil-cpp"] 2 | path = abseil-cpp 3 | url = https://github.com/abseil/abseil-cpp 4 | [submodule "googletest"] 5 | path = googletest 6 | url = https://github.com/google/googletest 7 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # 2 | 3 | > It's a good idea to open an issue first for discussion. 4 | 5 | - [ ] Tests pass 6 | - [ ] Appropriate changes to README are included in PR -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Expected Behavior 2 | 3 | 4 | ## Actual Behavior 5 | 6 | 7 | ## Steps to Reproduce the Problem 8 | 9 | 1. 10 | 1. 11 | 1. 12 | 13 | ## Specifications 14 | 15 | - Version: 16 | - Platform: -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= 2 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 3 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 4 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 5 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: 1.16 20 | 21 | - name: Build 22 | run: go build -v ./... 23 | 24 | - name: Test 25 | run: go test -v ./... 26 | 27 | -------------------------------------------------------------------------------- /_demo/demo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/google/chrometracing" 7 | ) 8 | 9 | const ( 10 | tidMain = iota 11 | tidNetwork 12 | ) 13 | 14 | func networkRequest() { 15 | defer chrometracing.Event("networkRequest", tidNetwork).Done() 16 | time.Sleep(100 * time.Millisecond) 17 | } 18 | 19 | func main() { 20 | defer chrometracing.Flush() 21 | defer chrometracing.Event("main", tidMain).Done() 22 | networkRequest() 23 | time.Sleep(500 * time.Millisecond) 24 | } 25 | -------------------------------------------------------------------------------- /cc/chrometracing_init_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chrometracing.h" 16 | 17 | int 18 | main(int argc, char **argv) { 19 | chrometracing::InitializeLogFile(); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chrometracing: chrome://tracing trace_event files 2 | 3 | The chrometracing directory contains libraries for various programming languages 4 | that make it easy to generate per-process trace files that can be loaded into 5 | chrome://tracing. 6 | 7 | ## Implementation Status 8 | 9 | Language | Status 10 | ---------|------------------------------ 11 | C++ | Implemented 12 | Go | Implemented: [github.com/google/chrometracing](https://pkg.go.dev/github.com/google/chrometracing) 13 | Python | Implemented, [but not released](https://github.com/google/chrometracing/issues/7) 14 | Java | Implemented, [but not open-sourced](https://github.com/google/chrometracing/issues/6) 15 | 16 | ## Go demo 17 | 18 | See [the `_demo` 19 | directory](https://github.com/google/chrometracing/tree/main/_demo) for an 20 | example Go program. 21 | 22 | After running the demo program, load the resulting chrome trace file from 23 | [`os.TempDir()`](https://golang.org/pkg/os/#TempDir) (e.g. `/tmp` on Linux) into 24 | `chrome://tracing`: 25 | 26 | ![screenshot](_demo/demo.png) 27 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | cmake_minimum_required(VERSION 3.10) 16 | 17 | project(chrometracing VERSION 0.0) 18 | 19 | set(CMAKE_CXX_STANDARD 17) 20 | set(CMAKE_CXX_STANDARD_REQUIRED True) 21 | 22 | # Allow linking against libraries from other directories. 23 | cmake_policy(SET CMP0079 NEW) 24 | # Allow overriding options with a regular variable. 25 | cmake_policy(SET CMP0077 NEW) 26 | 27 | enable_testing() 28 | 29 | set(INSTALL_GTEST off CACHE BOOL "Install googletest") 30 | 31 | add_subdirectory(abseil-cpp) 32 | add_subdirectory(googletest) 33 | add_subdirectory(cc) 34 | 35 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code Reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google/conduct/). 29 | -------------------------------------------------------------------------------- /cc/compat.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include "absl/strings/substitute.h" 20 | 21 | #include "chrometracing.h" 22 | 23 | namespace chrometracing { 24 | 25 | std::string ProcessName(pid_t pid) { 26 | if (!pid) 27 | pid = getpid(); 28 | std::string filename = absl::Substitute("/proc/$0/comm", pid); 29 | 30 | int fd = open(filename.c_str(), O_RDONLY); 31 | if (fd == -1) 32 | return {}; 33 | char buf[64]; 34 | int len = read(fd, buf, sizeof (buf)); 35 | close(fd); 36 | if (len == -1) 37 | return {}; 38 | if (len > 0 && buf[len-1] == '\n') 39 | len--; 40 | return std::string(buf, len); 41 | } 42 | 43 | 44 | //} // namespace chrometracing 45 | 46 | } 47 | -------------------------------------------------------------------------------- /cc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | add_library(chrometracing 16 | chrometracing.cc 17 | compat.cc) 18 | target_link_libraries(chrometracing absl::base absl::strings) 19 | target_include_directories(chrometracing PUBLIC 20 | "${PROJECT_SOURCE_DIR}/abseil-cpp/absl") 21 | 22 | add_executable(chrometracing_init_test chrometracing_init_test.cc) 23 | target_link_libraries(chrometracing_init_test chrometracing absl::base absl::strings absl::time) 24 | add_test(init chrometracing_init_test) 25 | 26 | add_executable(chrometracing_test chrometracing_test.cc) 27 | target_link_libraries(chrometracing_test chrometracing absl::base absl::strings absl::time gmock) 28 | add_test(chrometracing_test chrometracing_test) 29 | 30 | 31 | install(TARGETS chrometracing DESTINATION lib) 32 | install(FILES chrometracing.h DESTINATION include) 33 | -------------------------------------------------------------------------------- /traceinternal/traceinternal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package traceinternal contains chrome://tracing data structures for internal 16 | // use. 17 | package traceinternal 18 | 19 | // ViewerEvent is the main data structure, representing any trace event. 20 | // 21 | // Copied from go/gc/src/cmd/trace/trace.go 22 | type ViewerEvent struct { 23 | Name string `json:"name,omitempty"` 24 | Phase string `json:"ph"` 25 | Scope string `json:"s,omitempty"` 26 | Time float64 `json:"ts"` 27 | Dur float64 `json:"dur,omitempty"` 28 | Pid uint64 `json:"pid"` 29 | Tid uint64 `json:"tid"` 30 | ID uint64 `json:"id,omitempty"` 31 | Stack int `json:"sf,omitempty"` 32 | EndStack int `json:"esf,omitempty"` 33 | Arg interface{} `json:"args,omitempty"` 34 | Cname string `json:"cname,omitempty"` 35 | Category string `json:"cat,omitempty"` 36 | } 37 | -------------------------------------------------------------------------------- /cmd/combine/combinetraces.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Tool combinetraces combines multiple chrome://tracing trace files into a 16 | // whole-system view trace file. 17 | // 18 | // The tool identifies the top-level trace by looking at which trace file covers 19 | // the longest time span. Then, it recursively replaces trace spans matching 20 | // pid: (e.g. pid:1234) with the contents of the trace file for pid 1234 21 | // (based on the file name). 22 | package main 23 | 24 | import ( 25 | "flag" 26 | "fmt" 27 | "log" 28 | "os" 29 | "path/filepath" 30 | 31 | "github.com/google/chrometracing/combine" 32 | ) 33 | 34 | func combineTraces() error { 35 | if flag.NArg() < 1 { 36 | return fmt.Errorf("syntax: %s [...]", filepath.Base(os.Args[0])) 37 | } 38 | return combine.Traces(os.Stdout, flag.Args()) 39 | } 40 | 41 | func main() { 42 | flag.Parse() 43 | if err := combineTraces(); err != nil { 44 | log.Fatal(err) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: C++ with CMake 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | BUILD_TYPE: Debug 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | with: 19 | submodules: true 20 | 21 | - name: Create Build Environment 22 | # Some projects don't allow in-source building, so create a separate build directory 23 | # We'll use this as our working directory for all subsequent commands 24 | run: mkdir -p ${{github.workspace}}/build 25 | 26 | - name: Configure CMake 27 | # Use a bash shell so we can use the same syntax for environment variable 28 | # access regardless of the host operating system 29 | shell: bash 30 | working-directory: ${{github.workspace}}/build 31 | # Note the current convention is to use the -S and -B options here to specify source 32 | # and build directories, but this is only available with CMake 3.13 and higher. 33 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 34 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE 35 | 36 | - name: Build 37 | working-directory: ${{github.workspace}}/build 38 | shell: bash 39 | run: cmake --build . --config $BUILD_TYPE 40 | 41 | - name: Test 42 | working-directory: ${{github.workspace}}/build 43 | shell: bash 44 | # Execute tests defined by the CMake configuration. 45 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 46 | run: ctest -C $BUILD_TYPE 47 | 48 | -------------------------------------------------------------------------------- /cc/chrometracing_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chrometracing.h" 16 | 17 | #include 18 | #include 19 | 20 | namespace { 21 | 22 | using testing::StrEq; 23 | namespace internal = chrometracing::internal; 24 | 25 | TEST(ChromeTracing, EventRenderCorrectly) { 26 | EXPECT_THAT( 27 | internal::RenderEvent(internal::TraceEvent{ 28 | .name = "process_name", 29 | .phase = internal::Phase::METADATA, 30 | .pid = 42, 31 | .tid = 4242, 32 | .time = 0, 33 | .process_name = "some\"awful\n name\\to escape", 34 | }), 35 | StrEq( 36 | R"json({name: "process_name", "ph": "M", "pid": 42, "tid": 4242, "args": {"name": "some\"awful\n name\\to escape"}, }, 37 | )json")); 38 | EXPECT_THAT( 39 | internal::RenderEvent(internal::TraceEvent{ 40 | .name = "process_name", 41 | .phase = internal::Phase::BEGIN, 42 | .pid = 42, 43 | .tid = 4242, 44 | .time = 32767, 45 | }), 46 | StrEq( 47 | R"json({name: "process_name", "ph": "B", "pid": 42, "tid": 4242, "time": 32767, }, 48 | )json")); 49 | } 50 | 51 | } // namespace 52 | 53 | int main(int argc, char **argv) { 54 | ::testing::InitGoogleMock(&argc, argv); 55 | return RUN_ALL_TESTS(); 56 | } 57 | 58 | -------------------------------------------------------------------------------- /cc/chrometracing.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef CHROMETRACING_CHROMETRACING_H_ 16 | #define CHROMETRACING_CHROMETRACING_H_ 17 | // Offers Chrome tracing support for C++. Example use: 18 | // 19 | // { 20 | // auto e = chrometracing::Event("frog blast the vent core"); 21 | // 22 | // // actually frog blast the vent core 23 | // 24 | // // when e falls out of scope the event span will get closed automatically 25 | // } 26 | // 27 | // See go/chrometracing for more details on how tracing works. 28 | 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | #include "absl/base/internal/sysinfo.h" 36 | 37 | namespace chrometracing { 38 | namespace internal { 39 | 40 | enum class Phase: char { 41 | BEGIN = 'B', 42 | END = 'E', 43 | METADATA = 'M', 44 | }; 45 | 46 | struct TraceEvent { 47 | std::string name; 48 | Phase phase; 49 | int64_t pid; 50 | int64_t tid; 51 | int64_t time; 52 | std::optional process_name; 53 | }; 54 | 55 | std::string RenderEvent(TraceEvent e); 56 | } // namespace internal 57 | 58 | void InitializeLogFile(); 59 | 60 | class PendingEvent { 61 | public: 62 | PendingEvent(std::string name, pid_t tid) 63 | : name_(std::move(name)), tid_(tid) {} 64 | ~PendingEvent(); 65 | 66 | private: 67 | std::string name_; 68 | pid_t tid_; 69 | }; 70 | 71 | PendingEvent Event(std::string name, int64_t explicit_tid); 72 | PendingEvent Event(std::string name); 73 | 74 | 75 | // Compatibility definitions: 76 | 77 | std::string ProcessName(pid_t pid); 78 | 79 | 80 | } // namespace chrometracing 81 | 82 | #endif // CHROMETRACING_CHROMETRACING_H_ 83 | -------------------------------------------------------------------------------- /chrometracing.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # -*- coding: utf-8 -*- 16 | """Writes per-process Chrome trace_event files, for chrome://tracing.""" 17 | 18 | import json 19 | import os 20 | import os.path 21 | import sys 22 | import time 23 | 24 | 25 | def _open_trace_file(): 26 | """Opens the per-process trace file.""" 27 | in_test = os.getenv('TEST_TMPDIR') 28 | explicitly_enabled = os.getenv('CHROMETRACING_DIR') 29 | enable_tracing = in_test or explicitly_enabled 30 | if not enable_tracing: 31 | return None 32 | output_dir = os.getenv( 33 | 'TEST_UNDECLARED_OUTPUTS_DIR', 34 | default=os.getenv('CHROMETRACING_DIR', default='/usr/local/google/tmp')) 35 | fn = os.path.join( 36 | output_dir, 37 | 'ctrace.%s.%d.trace' % (os.path.basename(sys.argv[0]), os.getpid())) 38 | f = open(fn, mode='w') 39 | # We only ever open a JSON array. Ending the array is optional as per 40 | # go/trace_event so that not cleanly finished traces can still be read. 41 | f.write('[') 42 | return f 43 | 44 | 45 | traceFile = _open_trace_file() 46 | tracePid = os.getpid() 47 | traceStart = time.time() 48 | 49 | 50 | def microseconds_since_trace_start(): 51 | return (time.time() - traceStart) * 1000000 52 | 53 | 54 | def write_event(ev): 55 | if not traceFile: 56 | return 57 | traceFile.write(json.dumps(ev)) 58 | traceFile.write(',\n') 59 | 60 | 61 | write_event({ 62 | 'name': 'process_name', 63 | 'ph': 'M', # metadata event 64 | 'pid': tracePid, 65 | 'tid': tracePid, 66 | 'args': { 67 | 'name': ' '.join(sys.argv), 68 | }, 69 | }) 70 | 71 | 72 | class PendingEvent(object): 73 | """Pending trace event (not yet completed).""" 74 | 75 | def __init__(self, name, tid): 76 | self.name = name 77 | self.tid = tid 78 | 79 | def done(self): 80 | write_event({ 81 | 'name': self.name, 82 | 'ph': 'E', # Phase: End 83 | 'pid': tracePid, 84 | 'tid': self.tid, 85 | 'ts': microseconds_since_trace_start(), 86 | }) 87 | _release_tid(self.tid) 88 | 89 | 90 | def event(name): 91 | tid = _tid() 92 | write_event({ 93 | 'name': name, 94 | 'ph': 'B', # Phase: Begin 95 | 'pid': tracePid, 96 | 'tid': tid, 97 | 'ts': microseconds_since_trace_start(), 98 | }) 99 | return PendingEvent(name, tid) 100 | 101 | 102 | def flush(): 103 | """Flushes the trace file to disk.""" 104 | if not traceFile: 105 | return 106 | traceFile.flush() 107 | 108 | # tids is a chrome://tracing thread id pool. Python does not have threads or 109 | # thread ids (unlike e.g. Java or C++, but similar to Go), so we need to 110 | # maintain our own identifier. The chrome://tracing file format requires a 111 | # numeric thread id, so we just increment whenever we need a thread id, and 112 | # reuse the ones no longer in use. 113 | tids = [] 114 | 115 | 116 | def _tid(): 117 | # Re-use released tids if any: 118 | for tid, used in enumerate(tids): 119 | if not used: 120 | tids[tid] = True 121 | return tid 122 | tid = len(tids) 123 | tids.append(True) 124 | return tid 125 | 126 | 127 | def _release_tid(tid): 128 | tids[tid] = False 129 | -------------------------------------------------------------------------------- /combine/combine.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package combine combines multiple chrome://tracing trace files into a 16 | // whole-system view trace file. 17 | package combine 18 | 19 | import ( 20 | "bytes" 21 | "encoding/json" 22 | "fmt" 23 | "io" 24 | "io/ioutil" 25 | "strconv" 26 | "strings" 27 | 28 | "github.com/google/chrometracing/traceinternal" 29 | ) 30 | 31 | func loadTrace(path string) ([]traceinternal.ViewerEvent, error) { 32 | b, err := ioutil.ReadFile(path) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | b = bytes.TrimSpace(b) 38 | 39 | // For parsing the JSON with the stricter encoding/json package, remove the 40 | // trailing comma and complete the array, if needed: 41 | b = bytes.TrimSuffix(b, []byte{','}) 42 | if !bytes.HasSuffix(b, []byte{']'}) { 43 | b = append(b, ']') 44 | } 45 | 46 | var events []traceinternal.ViewerEvent 47 | if err := json.Unmarshal(b, &events); err != nil { 48 | return nil, err 49 | } 50 | return events, nil 51 | } 52 | 53 | type traceFiles struct { 54 | eventsForFn map[string][]traceinternal.ViewerEvent 55 | fnForPID map[int]string 56 | } 57 | 58 | func (t *traceFiles) replacePIDWithEvents(root []traceinternal.ViewerEvent) ([]traceinternal.ViewerEvent, error) { 59 | replaced := make([]traceinternal.ViewerEvent, 0, len(root)) 60 | for _, ev := range root { 61 | replaced = append(replaced, ev) 62 | if !strings.HasPrefix(ev.Name, "pid:") || 63 | ev.Phase != "B" { 64 | continue 65 | } 66 | pid, err := strconv.Atoi(strings.TrimPrefix(ev.Name, "pid:")) 67 | if err != nil { 68 | return nil, fmt.Errorf("BUG: pid: prefix followed by invalid pid: %q", ev.Name) 69 | } 70 | 71 | insertEvents := t.eventsForFn[t.fnForPID[pid]] 72 | for idx, iev := range insertEvents { 73 | iev.Time = ev.Time + iev.Time 74 | insertEvents[idx] = iev 75 | } 76 | insertEvents, err = t.replacePIDWithEvents(insertEvents) 77 | if err != nil { 78 | return nil, err 79 | } 80 | replaced = append(replaced, insertEvents...) 81 | } 82 | return replaced, nil 83 | } 84 | 85 | // Traces reads the specified chrome://tracing trace files and combines them 86 | // into a whole-system view trace file. 87 | func Traces(w io.Writer, filepaths []string) error { 88 | var ( 89 | fnForPID = make(map[int]string) 90 | eventsForFn = make(map[string][]traceinternal.ViewerEvent) 91 | 92 | highestTimestamp float64 93 | highestTimestampFile string 94 | ) 95 | for _, fn := range filepaths { 96 | events, err := loadTrace(fn) 97 | if err != nil { 98 | return err 99 | } 100 | eventsForFn[fn] = events 101 | for _, ev := range events { 102 | if ev.Time > highestTimestamp { 103 | highestTimestamp = ev.Time 104 | highestTimestampFile = fn 105 | } 106 | } 107 | 108 | parts := strings.Split(fn, ".") 109 | if parts[len(parts)-1] != "trace" { 110 | continue 111 | } 112 | pid, err := strconv.Atoi(parts[len(parts)-2]) 113 | if err != nil { 114 | return err 115 | } 116 | fnForPID[pid] = fn 117 | } 118 | t := &traceFiles{ 119 | eventsForFn: eventsForFn, 120 | fnForPID: fnForPID, 121 | } 122 | root, err := t.replacePIDWithEvents(eventsForFn[highestTimestampFile]) 123 | if err != nil { 124 | return err 125 | } 126 | b, err := json.Marshal(&root) 127 | if err != nil { 128 | return err 129 | } 130 | _, err = w.Write(b) 131 | return err 132 | } 133 | -------------------------------------------------------------------------------- /combine/combine_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package combine 16 | 17 | import ( 18 | "bytes" 19 | "encoding/json" 20 | "io/ioutil" 21 | "path/filepath" 22 | "testing" 23 | 24 | "github.com/google/chrometracing/traceinternal" 25 | 26 | "github.com/google/go-cmp/cmp" 27 | ) 28 | 29 | func TestLoadTrace(t *testing.T) { 30 | t.Run("Incomplete", func(t *testing.T) { 31 | const incompleteTrace = `[{"ph":"B"},` 32 | fn := filepath.Join(t.TempDir(), "incomplete.trace") 33 | if err := ioutil.WriteFile(fn, []byte(incompleteTrace), 0644); err != nil { 34 | t.Fatal(err) 35 | } 36 | events, err := loadTrace(fn) 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | if got, want := len(events), 1; got != want { 41 | t.Errorf("unexpected number of events loaded: got %d, want %d", got, want) 42 | } 43 | }) 44 | 45 | t.Run("Complete", func(t *testing.T) { 46 | const completeTrace = `[{"ph":"B"}]` 47 | fn := filepath.Join(t.TempDir(), "complete.trace") 48 | if err := ioutil.WriteFile(fn, []byte(completeTrace), 0644); err != nil { 49 | t.Fatal(err) 50 | } 51 | events, err := loadTrace(fn) 52 | if err != nil { 53 | t.Fatal(err) 54 | } 55 | if got, want := len(events), 1; got != want { 56 | t.Errorf("unexpected number of events loaded: got %d, want %d", got, want) 57 | } 58 | }) 59 | } 60 | 61 | func writeEvents(t *testing.T, filename string, events []traceinternal.ViewerEvent) { 62 | t.Helper() 63 | 64 | b, err := json.Marshal(events) 65 | if err != nil { 66 | t.Fatal(err) 67 | } 68 | if err := ioutil.WriteFile(filename, b, 0644); err != nil { 69 | t.Fatal(err) 70 | } 71 | } 72 | 73 | const ( 74 | begin = "B" 75 | end = "E" 76 | ) 77 | 78 | func span(name string, pid uint64, from, to float64) []traceinternal.ViewerEvent { 79 | return []traceinternal.ViewerEvent{ 80 | { 81 | Name: name, 82 | Phase: begin, 83 | Pid: pid, 84 | Tid: pid, 85 | Time: from, 86 | }, 87 | { 88 | Name: name, 89 | Phase: end, 90 | Pid: pid, 91 | Tid: pid, 92 | Time: to, 93 | }, 94 | } 95 | 96 | } 97 | 98 | func TestCombine(t *testing.T) { 99 | startupEvents := span("startup", 1234, 0, 2000000) 100 | const embedStart = 2500000 101 | pidEvents := span("pid:5678", 1234, embedStart, 5000000) 102 | shutdownEvents := span("shutdown", 1234, 6000000, 7000000) 103 | 104 | topLevelEvents := append(append(append([]traceinternal.ViewerEvent{}, startupEvents...), pidEvents...), shutdownEvents...) 105 | topLevelFn := filepath.Join(t.TempDir(), "chrometracing.1234.trace") 106 | writeEvents(t, topLevelFn, topLevelEvents) 107 | 108 | childEvents := append(span("frobnicate", 5678, 0, 1000000), 109 | span("quxnicate", 5678, 1100000, 2000000)...) 110 | childEventsEmbedded := append(span("frobnicate", 5678, embedStart+0, embedStart+1000000), 111 | span("quxnicate", 5678, embedStart+1100000, embedStart+2000000)...) 112 | 113 | childFn := filepath.Join(t.TempDir(), "chrometracing.5678.trace") 114 | writeEvents(t, childFn, childEvents) 115 | 116 | var buf bytes.Buffer 117 | if err := Traces(&buf, []string{childFn, topLevelFn}); err != nil { 118 | t.Fatal(err) 119 | } 120 | 121 | var events []traceinternal.ViewerEvent 122 | if err := json.Unmarshal(buf.Bytes(), &events); err != nil { 123 | t.Fatal(err) 124 | } 125 | 126 | wantEvents := append(append([]traceinternal.ViewerEvent{ 127 | startupEvents[0], 128 | startupEvents[1], 129 | 130 | pidEvents[0], 131 | }, childEventsEmbedded...), 132 | pidEvents[1], 133 | 134 | shutdownEvents[0], 135 | shutdownEvents[1]) 136 | if diff := cmp.Diff(wantEvents, events); diff != "" { 137 | t.Errorf("unexpected combination: diff (-want +got):\n%s", diff) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /cc/chrometracing.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chrometracing.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "absl/base/log_severity.h" 21 | #include "absl/strings/str_replace.h" 22 | #include "absl/strings/substitute.h" 23 | #include "absl/time/clock.h" 24 | 25 | namespace chrometracing { 26 | 27 | namespace internal { 28 | static FILE* log_file = nullptr; 29 | static pid_t my_pid = 0; 30 | static int64_t start_nanos = 0; 31 | 32 | std::string JSONEscape(const std::string& s) { 33 | return absl::StrReplaceAll(s, { 34 | {"\\", "\\\\"}, 35 | {"\"", "\\\""}, 36 | {"\n", "\\n"}, 37 | }); 38 | } 39 | 40 | std::string RenderEvent(TraceEvent e) { 41 | std::vector parts; 42 | parts.push_back(absl::Substitute( 43 | R"json({name: "$0", "ph": "$1", "pid": $2, "tid": $3, )json", 44 | JSONEscape(e.name), static_cast(e.phase), e.pid, e.tid)); 45 | if (e.time) { 46 | parts.push_back(absl::Substitute(R"json("time": $0, )json", e.time)); 47 | } 48 | if (e.process_name) { 49 | parts.push_back(absl::Substitute(R"json("args": {"name": "$0"}, )json", 50 | JSONEscape(*e.process_name))); 51 | } 52 | parts.push_back("},\n"); 53 | return absl::StrJoin(parts, ""); 54 | } 55 | 56 | void WriteEvent(TraceEvent e) { 57 | if (log_file) { 58 | const std::string s = RenderEvent(e); 59 | fwrite(s.data(), sizeof(char), s.size(), log_file); 60 | } 61 | } 62 | 63 | std::string GetDestDir() { 64 | const char* env_var = getenv("TEST_UNDECLARED_OUTPUTS_DIR"); 65 | if (env_var && env_var[0] != '\0') { 66 | return std::string(env_var); 67 | } 68 | return "."; 69 | } 70 | 71 | } // namespace internal 72 | 73 | void InitializeLogFile() { 74 | internal::start_nanos = absl::GetCurrentTimeNanos(); 75 | const std::string dest_dir = internal::GetDestDir(); 76 | internal::my_pid = getpid(); 77 | auto my_name = ProcessName(internal::my_pid); 78 | auto dest_path = 79 | absl::Substitute("$0/ctrace.$1.$2.trace", dest_dir, my_name, internal::my_pid); 80 | /*LOG(INFO) << "Writing Chrome trace_events (for chrome::tracing) to " 81 | << dest_path;*/ 82 | internal::log_file = fopen(dest_path.c_str(), "w"); 83 | if (!internal::log_file) { 84 | /*PLOG(INFO) << "Failed to open " << dest_path 85 | << " for Chrome trace events, continuing without tracing";*/ 86 | return; 87 | } 88 | fputs("[\n", internal::log_file); 89 | internal::WriteEvent(internal::TraceEvent{ 90 | .name = "process_name", 91 | .phase = internal::Phase::METADATA, 92 | .pid = internal::my_pid, 93 | .tid = absl::base_internal::GetTID(), 94 | .time = 0, 95 | .process_name = my_name, 96 | }); 97 | } 98 | 99 | 100 | PendingEvent::~PendingEvent() { 101 | WriteEvent(internal::TraceEvent{ 102 | .name = name_, 103 | .phase = internal::Phase::END, 104 | .pid = internal::my_pid, 105 | .tid = tid_, 106 | .time = ((absl::GetCurrentTimeNanos() - internal::start_nanos) / 1000), 107 | }); 108 | } 109 | 110 | PendingEvent Event(std::string name, int64_t tid) { 111 | WriteEvent(internal::TraceEvent{ 112 | .name = name, 113 | .phase = internal::Phase::BEGIN, 114 | .pid = internal::my_pid, 115 | .tid = tid, 116 | .time = ((absl::GetCurrentTimeNanos() - internal::start_nanos) / 1000), 117 | }); 118 | return PendingEvent(name, tid); 119 | } 120 | 121 | PendingEvent Event(std::string name) { 122 | return Event(name, absl::base_internal::GetTID()); 123 | } 124 | } // namespace chrometracing 125 | -------------------------------------------------------------------------------- /docs/code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of 9 | experience, education, socio-economic status, nationality, personal appearance, 10 | race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or reject 41 | comments, commits, code, wiki edits, issues, and other contributions that are 42 | not aligned to this Code of Conduct, or to ban temporarily or permanently any 43 | contributor for other behaviors that they deem inappropriate, threatening, 44 | offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | This Code of Conduct also applies outside the project spaces when the Project 56 | Steward has a reasonable belief that an individual's behavior may have a 57 | negative impact on the project or its community. 58 | 59 | ## Conflict Resolution 60 | 61 | We do not believe that all conflict is bad; healthy debate and disagreement 62 | often yield positive results. However, it is never okay to be disrespectful or 63 | to engage in behavior that violates the project’s code of conduct. 64 | 65 | If you see someone violating the code of conduct, you are encouraged to address 66 | the behavior directly with those involved. Many issues can be resolved quickly 67 | and easily, and this gives people more control over the outcome of their 68 | dispute. If you are unable to resolve the matter for any reason, or if the 69 | behavior is threatening or harassing, report it. We are dedicated to providing 70 | an environment where participants feel welcome and safe. 71 | 72 | Reports should be directed to *[PROJECT STEWARD NAME(s) AND EMAIL(s)]*, the 73 | Project Steward(s) for *[PROJECT NAME]*. It is the Project Steward’s duty to 74 | receive and address reported violations of the code of conduct. They will then 75 | work with a committee consisting of representatives from the Open Source 76 | Programs Office and the Google Open Source Strategy team. If for any reason you 77 | are uncomfortable reaching out to the Project Steward, please email 78 | opensource@google.com. 79 | 80 | We will investigate every complaint, but you may not receive a direct response. 81 | We will use our discretion in determining when and how to follow up on reported 82 | incidents, which may range from not taking action to permanent expulsion from 83 | the project and project-sponsored spaces. We will notify the accused of the 84 | report and provide them an opportunity to discuss it before any action is taken. 85 | The identity of the reporter will be omitted from the details of the report 86 | supplied to the accused. In potentially harmful situations, such as ongoing 87 | harassment or threats to anyone's safety, we may take action without notice. 88 | 89 | ## Attribution 90 | 91 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, 92 | available at 93 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 94 | -------------------------------------------------------------------------------- /chrometracing.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package chrometracing writes per-process Chrome trace_event files that can be 16 | // loaded into chrome://tracing. 17 | package chrometracing 18 | 19 | import ( 20 | "encoding/json" 21 | "fmt" 22 | "os" 23 | "path/filepath" 24 | "strings" 25 | "sync" 26 | "time" 27 | 28 | "github.com/google/chrometracing/traceinternal" 29 | ) 30 | 31 | var trace = struct { 32 | start time.Time 33 | pid uint64 34 | 35 | fileMu sync.Mutex 36 | file *os.File 37 | }{ 38 | pid: uint64(os.Getpid()), 39 | } 40 | 41 | var out = setup(false) 42 | 43 | // Path returns the full path of the chrome://tracing trace_event file for 44 | // display in log messages. 45 | func Path() string { return out } 46 | 47 | // EnableTracing turns on tracing, regardless of running in a test or 48 | // not. Tracing is enabled by default if the CHROMETRACING_DIR environment 49 | // variable is present and non-empty. 50 | func EnableTracing() { 51 | trace.fileMu.Lock() 52 | alreadyEnabled := trace.file != nil 53 | trace.fileMu.Unlock() 54 | if alreadyEnabled { 55 | return 56 | } 57 | out = setup(true) 58 | } 59 | 60 | func setup(overrideEnable bool) string { 61 | inTest := os.Getenv("TEST_TMPDIR") != "" 62 | explicitlyEnabled := os.Getenv("CHROMETRACING_DIR") != "" 63 | enableTracing := inTest || explicitlyEnabled || overrideEnable 64 | if !enableTracing { 65 | return "" 66 | } 67 | 68 | var err error 69 | dir := os.Getenv("TEST_UNDECLARED_OUTPUTS_DIR") 70 | if dir == "" { 71 | dir = os.Getenv("CHROMETRACING_DIR") 72 | } 73 | if dir == "" { 74 | dir = os.TempDir() 75 | } 76 | fn := filepath.Join(dir, fmt.Sprintf("%s.%d.trace", filepath.Base(os.Args[0]), trace.pid)) 77 | trace.file, err = os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC|os.O_EXCL, 0644) 78 | if err != nil { 79 | // Using the log package from func init results in an error message 80 | // being printed. 81 | fmt.Fprintf(os.Stderr, "continuing without tracing: %v\n", err) 82 | return "" 83 | } 84 | 85 | // We only ever open a JSON array. Ending the array is optional as per 86 | // go/trace_event so that not cleanly finished traces can still be read. 87 | trace.file.Write([]byte{'['}) 88 | trace.start = time.Now() 89 | 90 | writeEvent(&traceinternal.ViewerEvent{ 91 | Name: "process_name", 92 | Phase: "M", // Metadata Event 93 | Pid: trace.pid, 94 | Tid: trace.pid, 95 | Arg: struct { 96 | Name string `json:"name"` 97 | }{ 98 | Name: strings.Join(os.Args, " "), 99 | }, 100 | }) 101 | return fn 102 | } 103 | 104 | func writeEvent(ev *traceinternal.ViewerEvent) { 105 | b, err := json.Marshal(&ev) 106 | if err != nil { 107 | fmt.Fprintf(os.Stderr, "%v\n", err) 108 | return 109 | } 110 | trace.fileMu.Lock() 111 | defer trace.fileMu.Unlock() 112 | if _, err = trace.file.Write(b); err != nil { 113 | fmt.Fprintf(os.Stderr, "%v\n", err) 114 | return 115 | } 116 | if _, err = trace.file.Write([]byte{',', '\n'}); err != nil { 117 | fmt.Fprintf(os.Stderr, "%v\n", err) 118 | return 119 | } 120 | } 121 | 122 | const ( 123 | begin = "B" 124 | end = "E" 125 | ) 126 | 127 | // A PendingEvent represents an ongoing unit of work. The begin trace event has 128 | // already been written, and calling Done will write the end trace event. 129 | type PendingEvent struct { 130 | name string 131 | tid uint64 132 | } 133 | 134 | // Done writes the end trace event for this unit of work. 135 | func (pe *PendingEvent) Done() { 136 | if pe == nil || pe.name == "" || trace.file == nil { 137 | return 138 | } 139 | writeEvent(&traceinternal.ViewerEvent{ 140 | Name: pe.name, 141 | Phase: end, 142 | Pid: trace.pid, 143 | Tid: pe.tid, 144 | Time: float64(time.Since(trace.start).Microseconds()), 145 | }) 146 | releaseTid(pe.tid) 147 | } 148 | 149 | // Event logs a unit of work. To instrument a Go function, use e.g.: 150 | // 151 | // func calcPi() { 152 | // defer chrometracing.Event("calculate pi").Done() 153 | // // … 154 | // } 155 | // 156 | // For more finely-granular traces, use e.g.: 157 | // 158 | // for _, cmd := range commands { 159 | // ev := chrometracing.Event("initialize " + cmd.Name) 160 | // cmd.Init() 161 | // ev.Done() 162 | // } 163 | func Event(name string) *PendingEvent { 164 | if trace.file == nil { 165 | return &PendingEvent{} 166 | } 167 | tid := tid() 168 | writeEvent(&traceinternal.ViewerEvent{ 169 | Name: name, 170 | Phase: begin, 171 | Pid: trace.pid, 172 | Tid: tid, 173 | Time: float64(time.Since(trace.start).Microseconds()), 174 | }) 175 | return &PendingEvent{ 176 | name: name, 177 | tid: tid, 178 | } 179 | } 180 | 181 | // tids is a chrome://tracing thread id pool. Go does not permit accessing the 182 | // goroutine id, so we need to maintain our own identifier. The chrome://tracing 183 | // file format requires a numeric thread id, so we just increment whenever we 184 | // need a thread id, and reuse the ones no longer in use. 185 | // 186 | // In practice, parallelized sections of the code (many goroutines) end up using 187 | // only as few thread ids as are concurrently in use, and the rest of the events 188 | // mirror the code call stack nicely. See e.g. http://screen/7MPcAcvXQNUE3JZ 189 | var tids struct { 190 | sync.Mutex 191 | 192 | // We allocate chrome://tracing thread ids based on the index of the 193 | // corresponding entry in the used slice. 194 | used []bool 195 | 196 | // next points to the earliest unused tid to consider for the next tid to 197 | // hand out. This is purely a performance optimization to avoid O(n) slice 198 | // iteration. 199 | next int 200 | } 201 | 202 | func tid() uint64 { 203 | tids.Lock() 204 | defer tids.Unlock() 205 | // re-use released tids if any 206 | for t := tids.next; t < len(tids.used); t++ { 207 | if !tids.used[t] { 208 | tids.used[t] = true 209 | tids.next = t + 1 210 | return uint64(t) 211 | } 212 | } 213 | // allocate a new tid 214 | t := len(tids.used) 215 | tids.used = append(tids.used, true) 216 | tids.next = t + 1 217 | return uint64(t) 218 | } 219 | 220 | func releaseTid(t uint64) { 221 | tids.Lock() 222 | defer tids.Unlock() 223 | tids.used[int(t)] = false 224 | if tids.next > int(t) { 225 | tids.next = int(t) 226 | } 227 | } 228 | 229 | // Flush should be called before your program terminates, and/or periodically 230 | // for long-running programs, to flush any pending chrome://tracing events out 231 | // to disk. 232 | func Flush() error { 233 | trace.fileMu.Lock() 234 | defer trace.fileMu.Unlock() 235 | if err := trace.file.Sync(); err != nil { 236 | return fmt.Errorf("flushing trace file: %v", err) 237 | } 238 | return nil 239 | } 240 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------