mirror of
https://github.com/golang/go
synced 2024-11-18 13:04:46 -07:00
cmd/heapview: delete
This is all dead code that never went anywhere. Change-Id: I79db05d6391709bb9ae3d86f557246f1dd3b21f7 Reviewed-on: https://go-review.googlesource.com/c/159957 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
d66bd3c5d5
commit
3c4dfba64b
@ -1 +0,0 @@
|
|||||||
BasedOnStyle: Google
|
|
1
cmd/heapview/client/.gitignore
vendored
1
cmd/heapview/client/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/node_modules/
|
|
@ -1,45 +0,0 @@
|
|||||||
# Go Heap Viewer Client
|
|
||||||
|
|
||||||
This directory contains the client Typescript code for the Go
|
|
||||||
heap viewer.
|
|
||||||
|
|
||||||
## Typescript Tooling
|
|
||||||
|
|
||||||
Below are instructions for downloading tooling and files to
|
|
||||||
help make the development process more convenient. These tools
|
|
||||||
are not required for contributing or running the heap viewer-
|
|
||||||
they are just meant as development aids.
|
|
||||||
|
|
||||||
## Node and NPM
|
|
||||||
|
|
||||||
We use npm to manage the dependencies for these tools. There are
|
|
||||||
a couple of ways of installing npm on your system, but we recommend
|
|
||||||
using nvm.
|
|
||||||
|
|
||||||
Run the following command to install nvm:
|
|
||||||
|
|
||||||
[shell]$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.3/install.sh | bash
|
|
||||||
|
|
||||||
or see the instructions on [the nvm github page](github.com/creationix/nvm)
|
|
||||||
for alternative methods. This will put the nvm tool in your home directory
|
|
||||||
and edit your path to add nvm, node and other tools you install using them.
|
|
||||||
Once nvm is installed, use
|
|
||||||
|
|
||||||
[shell]$ nvm install node
|
|
||||||
|
|
||||||
then
|
|
||||||
|
|
||||||
[shell]$ nvm use node
|
|
||||||
|
|
||||||
to install node.js.
|
|
||||||
|
|
||||||
Once node is installed, you can install typescript using
|
|
||||||
|
|
||||||
[shell]$ npm install -g typescript
|
|
||||||
|
|
||||||
Finally, import type definitions into this project by running
|
|
||||||
|
|
||||||
[shell]$ npm install
|
|
||||||
|
|
||||||
in this directory. They will be imported into the node_packages directory
|
|
||||||
and be automatically available to the Typescript compiler.
|
|
@ -1,195 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An enum of types of actions that might be requested
|
|
||||||
* by the app.
|
|
||||||
*/
|
|
||||||
enum Action {
|
|
||||||
TOGGLE_SIDEBAR, // Toggle the sidebar.
|
|
||||||
NAVIGATE_ABOUT, // Go to the about page.
|
|
||||||
}
|
|
||||||
|
|
||||||
const TITLE = 'Go Heap Viewer';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A type of event that signals to the AppElement controller
|
|
||||||
* that something shoud be done. For the most part, the structure
|
|
||||||
* of the app will be that elements' state will mostly be controlled
|
|
||||||
* by parent elements. Elements will issue actions that the AppElement
|
|
||||||
* will handle, and the app will be re-rendered down the DOM
|
|
||||||
* hierarchy.
|
|
||||||
*/
|
|
||||||
class ActionEvent extends Event {
|
|
||||||
static readonly EVENT_TYPE = 'action-event'
|
|
||||||
constructor(public readonly action: Action) { super(ActionEvent.EVENT_TYPE); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A hamburger menu element. Triggers a TOGGLE_SIDE action to toggle the
|
|
||||||
* sidebar.
|
|
||||||
*/
|
|
||||||
export class HamburgerElement extends HTMLElement {
|
|
||||||
static readonly NAME = 'heap-hamburger';
|
|
||||||
|
|
||||||
createdCallback() {
|
|
||||||
this.appendChild(document.createTextNode('☰'));
|
|
||||||
this.onclick =
|
|
||||||
() => { this.dispatchEvent(new ActionEvent(Action.TOGGLE_SIDEBAR)) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.registerElement(HamburgerElement.NAME, HamburgerElement);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A heading for the page with a hamburger menu and a title.
|
|
||||||
*/
|
|
||||||
export class HeadingElement extends HTMLElement {
|
|
||||||
static readonly NAME = 'heap-heading';
|
|
||||||
|
|
||||||
createdCallback() {
|
|
||||||
this.style.display = 'block';
|
|
||||||
this.style.backgroundColor = '#2196F3';
|
|
||||||
this.style.webkitUserSelect = 'none';
|
|
||||||
this.style.cursor = 'default';
|
|
||||||
this.style.color = '#FFFFFF';
|
|
||||||
this.style.padding = '10px';
|
|
||||||
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.style.margin = '0px';
|
|
||||||
div.style.fontSize = '2em';
|
|
||||||
div.appendChild(document.createElement(HamburgerElement.NAME));
|
|
||||||
div.appendChild(document.createTextNode(' ' + TITLE));
|
|
||||||
this.appendChild(div);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.registerElement(HeadingElement.NAME, HeadingElement);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A sidebar that has navigation for the app.
|
|
||||||
*/
|
|
||||||
export class SidebarElement extends HTMLElement {
|
|
||||||
static readonly NAME = 'heap-sidebar';
|
|
||||||
|
|
||||||
createdCallback() {
|
|
||||||
this.style.display = 'none';
|
|
||||||
this.style.backgroundColor = '#9E9E9E';
|
|
||||||
this.style.width = '15em';
|
|
||||||
|
|
||||||
const aboutButton = document.createElement('button');
|
|
||||||
aboutButton.innerText = 'about';
|
|
||||||
aboutButton.onclick =
|
|
||||||
() => { this.dispatchEvent(new ActionEvent(Action.NAVIGATE_ABOUT)) };
|
|
||||||
this.appendChild(aboutButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle() {
|
|
||||||
this.style.display = this.style.display === 'none' ? 'block' : 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.registerElement(SidebarElement.NAME, SidebarElement);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Container for the main content in the app.
|
|
||||||
* TODO(matloob): Implement main content.
|
|
||||||
*/
|
|
||||||
export class MainContentElement extends HTMLElement {
|
|
||||||
static readonly NAME = 'heap-container';
|
|
||||||
|
|
||||||
attachedCallback() {
|
|
||||||
this.style.backgroundColor = '#E0E0E0';
|
|
||||||
this.style.height = '100%';
|
|
||||||
this.style.flex = '1';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.registerElement(MainContentElement.NAME, MainContentElement);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A container and controller for the whole app.
|
|
||||||
* Contains the heading, side drawer and main panel.
|
|
||||||
*/
|
|
||||||
class AppElement extends HTMLElement {
|
|
||||||
static readonly NAME = 'heap-app';
|
|
||||||
private sidebar: SidebarElement;
|
|
||||||
private mainContent: MainContentElement;
|
|
||||||
|
|
||||||
attachedCallback() {
|
|
||||||
document.title = TITLE;
|
|
||||||
|
|
||||||
this.addEventListener(
|
|
||||||
ActionEvent.EVENT_TYPE, e => this.handleAction(e as ActionEvent),
|
|
||||||
/* capture */ true);
|
|
||||||
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
this.style.display = 'block';
|
|
||||||
this.style.height = '100vh';
|
|
||||||
this.style.width = '100vw';
|
|
||||||
this.appendChild(document.createElement(HeadingElement.NAME));
|
|
||||||
|
|
||||||
const bodyDiv = document.createElement('div');
|
|
||||||
bodyDiv.style.height = '100%';
|
|
||||||
bodyDiv.style.display = 'flex';
|
|
||||||
this.sidebar =
|
|
||||||
document.createElement(SidebarElement.NAME) as SidebarElement;
|
|
||||||
bodyDiv.appendChild(this.sidebar);
|
|
||||||
this.mainContent =
|
|
||||||
document.createElement(MainContentElement.NAME) as MainContentElement;
|
|
||||||
bodyDiv.appendChild(this.mainContent);
|
|
||||||
this.appendChild(bodyDiv);
|
|
||||||
|
|
||||||
this.renderRoute();
|
|
||||||
}
|
|
||||||
|
|
||||||
renderRoute() {
|
|
||||||
this.mainContent.innerHTML = ''
|
|
||||||
switch (window.location.pathname) {
|
|
||||||
case '/about':
|
|
||||||
this.mainContent.appendChild(
|
|
||||||
document.createElement(AboutPageElement.NAME));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleAction(event: ActionEvent) {
|
|
||||||
switch (event.action) {
|
|
||||||
case Action.TOGGLE_SIDEBAR:
|
|
||||||
this.sidebar.toggle();
|
|
||||||
break;
|
|
||||||
case Action.NAVIGATE_ABOUT:
|
|
||||||
window.history.pushState({}, '', '/about');
|
|
||||||
this.renderRoute();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.registerElement(AppElement.NAME, AppElement);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An about page.
|
|
||||||
*/
|
|
||||||
class AboutPageElement extends HTMLElement {
|
|
||||||
static readonly NAME = 'heap-about';
|
|
||||||
|
|
||||||
createdCallback() { this.textContent = TITLE; }
|
|
||||||
}
|
|
||||||
document.registerElement(AboutPageElement.NAME, AboutPageElement);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets body's margin and padding, and sets font.
|
|
||||||
*/
|
|
||||||
function clearStyle(document: Document) {
|
|
||||||
const styleElement = document.createElement('style') as HTMLStyleElement;
|
|
||||||
document.head.appendChild(styleElement);
|
|
||||||
const styleSheet = styleElement.sheet as CSSStyleSheet;
|
|
||||||
styleSheet.insertRule(
|
|
||||||
'* {font-family: Roboto,Helvetica; box-sizing: border-box}', 0);
|
|
||||||
styleSheet.insertRule('body {margin: 0px; padding:0px}', 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
clearStyle(document);
|
|
||||||
document.body.appendChild(document.createElement(AppElement.NAME));
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import {HamburgerElement, HeadingElement, SidebarElement, main} from './main';
|
|
||||||
|
|
||||||
describe('main', () => {
|
|
||||||
it('sets the document\'s title', () => {
|
|
||||||
main();
|
|
||||||
expect(document.title).toBe('Go Heap Viewer');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a heading', () => {
|
|
||||||
main();
|
|
||||||
expect(document.querySelector(HeadingElement.NAME)).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a sidebar', () => {
|
|
||||||
main();
|
|
||||||
const hamburger = document.querySelector(HamburgerElement.NAME);
|
|
||||||
const sidebar =
|
|
||||||
document.querySelector(SidebarElement.NAME) as SidebarElement;
|
|
||||||
expect(sidebar.style.display).toBe('none');
|
|
||||||
|
|
||||||
// Click on the hamburger. Sidebar should then be visible.
|
|
||||||
hamburger.dispatchEvent(new Event('click'));
|
|
||||||
expect(sidebar.style.display).toBe('block');
|
|
||||||
})
|
|
||||||
});
|
|
@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"//": [
|
|
||||||
"Copyright 2016 The Go Authors. All rights reserved.",
|
|
||||||
"Use of this source code is governed by a BSD-style",
|
|
||||||
"license that can be found in the LICENSE file.",
|
|
||||||
|
|
||||||
"This file exists to help import typescript typings",
|
|
||||||
"for web features used in this project. Neither the",
|
|
||||||
"typings nor node or npm are required to do development",
|
|
||||||
"on the code in this project.",
|
|
||||||
|
|
||||||
"If you do have npm installed, use the `npm i` command",
|
|
||||||
"in this directory to install the typings."
|
|
||||||
],
|
|
||||||
"private": true,
|
|
||||||
"name": "@golangtools/heapview",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/webcomponents.js": "latest",
|
|
||||||
"@types/whatwg-fetch": "latest",
|
|
||||||
"@types/jasmine": "latest",
|
|
||||||
|
|
||||||
"jasmine-core": "latest",
|
|
||||||
"karma": "latest",
|
|
||||||
"karma-jasmine": "latest",
|
|
||||||
"karma-chrome-launcher": "latest",
|
|
||||||
|
|
||||||
"clang-format": "latest"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"test": "karma start testing/karma.conf.js",
|
|
||||||
"format": "find . | grep '\\(test_main\\.js\\|\\.ts\\)$' | xargs clang-format -i",
|
|
||||||
"lint": "tslint --project ."
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
module.exports = config => {
|
|
||||||
config.set({
|
|
||||||
frameworks: ['jasmine'],
|
|
||||||
basePath: '../../../..',
|
|
||||||
files: [
|
|
||||||
'third_party/webcomponents/customelements.js',
|
|
||||||
'third_party/typescript/typescript.js',
|
|
||||||
'third_party/moduleloader/moduleloader.js',
|
|
||||||
'cmd/heapview/client/testing/test_main.js',
|
|
||||||
{pattern: 'cmd/heapview/client/**/*.ts', included: false},
|
|
||||||
],
|
|
||||||
browsers: ['Chrome'],
|
|
||||||
plugins: [
|
|
||||||
'karma-jasmine',
|
|
||||||
'karma-chrome-launcher'
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Configure module loader.
|
|
||||||
System.transpiler = 'typescript'
|
|
||||||
System.typescriptOptions = {
|
|
||||||
target: ts.ScriptTarget.ES2015
|
|
||||||
};
|
|
||||||
System.locate = (load) => load.name + '.ts';
|
|
||||||
|
|
||||||
// Determine set of test files.
|
|
||||||
var tests = [];
|
|
||||||
for (var file in window.__karma__.files) {
|
|
||||||
if (window.__karma__.files.hasOwnProperty(file)) {
|
|
||||||
if (/_test\.ts$/.test(file)) {
|
|
||||||
tests.push(file.slice(0, -3));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Steal loaded callback so we can block until we're
|
|
||||||
// done loading all test modules.
|
|
||||||
var loadedCallback = window.__karma__.loaded.bind(window.__karma__);
|
|
||||||
window.__karma__.loaded = () => {};
|
|
||||||
|
|
||||||
// Load all test modules, and then call loadedCallback.
|
|
||||||
var promises = [];
|
|
||||||
for (var i = 0; i < tests.length; i++) {
|
|
||||||
promises.push(System.import(tests[i]));
|
|
||||||
}
|
|
||||||
Promise.all(promises).then(loadedCallback);
|
|
@ -1,16 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// This file contains configuration for the Typescript
|
|
||||||
// compiler if you're running it locally for typechecking
|
|
||||||
// and other tooling. The Typescript compiler is
|
|
||||||
// not necessary to do development on this project.
|
|
||||||
|
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"noEmit": true,
|
|
||||||
"strictNullChecks": true,
|
|
||||||
"target": "es2015"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// This tslint file is based on a configuration used at
|
|
||||||
// Google.
|
|
||||||
|
|
||||||
{
|
|
||||||
"rules": {
|
|
||||||
"class-name": true,
|
|
||||||
"forin": true,
|
|
||||||
"interface-name": [true, "never-prefix"],
|
|
||||||
"jsdoc-format": true,
|
|
||||||
"label-position": true,
|
|
||||||
"label-undefined": true,
|
|
||||||
"new-parens": true,
|
|
||||||
"no-angle-bracket-type-assertion": true,
|
|
||||||
"no-construct": true,
|
|
||||||
"no-debugger": true,
|
|
||||||
"no-namespace": [true, "allow-declarations"],
|
|
||||||
"no-reference": true,
|
|
||||||
"no-require-imports": true,
|
|
||||||
"no-unused-expression": true,
|
|
||||||
"no-unused-variable": true,
|
|
||||||
"no-use-before-declare": true,
|
|
||||||
"no-var-keyword": true,
|
|
||||||
"semicolon": [true, "always"],
|
|
||||||
"switch-default": true,
|
|
||||||
"triple-equals": [true, "allow-null-check"],
|
|
||||||
"use-isnan": true,
|
|
||||||
"variable-name": [
|
|
||||||
true,
|
|
||||||
"check-format",
|
|
||||||
"ban-keywords",
|
|
||||||
"allow-leading-underscore",
|
|
||||||
"allow-trailing-underscore",
|
|
||||||
"allow-pascal-case"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,145 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build darwin linux
|
|
||||||
|
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
var errMmapClosed = errors.New("mmap: closed")
|
|
||||||
|
|
||||||
// mmapFile wraps a memory-mapped file.
|
|
||||||
type mmapFile struct {
|
|
||||||
data []byte
|
|
||||||
pos uint64
|
|
||||||
writable bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// mmapOpen opens the named file for reading.
|
|
||||||
// If writable is true, the file is also open for writing.
|
|
||||||
func mmapOpen(filename string, writable bool) (*mmapFile, error) {
|
|
||||||
f, err := os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
st, err := f.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
size := st.Size()
|
|
||||||
if size == 0 {
|
|
||||||
return &mmapFile{data: []byte{}}, nil
|
|
||||||
}
|
|
||||||
if size < 0 {
|
|
||||||
return nil, fmt.Errorf("mmap: file %q has negative size: %d", filename, size)
|
|
||||||
}
|
|
||||||
if size != int64(int(size)) {
|
|
||||||
return nil, fmt.Errorf("mmap: file %q is too large", filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
prot := syscall.PROT_READ
|
|
||||||
if writable {
|
|
||||||
prot |= syscall.PROT_WRITE
|
|
||||||
}
|
|
||||||
data, err := syscall.Mmap(int(f.Fd()), 0, int(size), prot, syscall.MAP_SHARED)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &mmapFile{data: data, writable: writable}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns the size of the mapped file.
|
|
||||||
func (f *mmapFile) Size() uint64 {
|
|
||||||
return uint64(len(f.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pos returns the current file pointer.
|
|
||||||
func (f *mmapFile) Pos() uint64 {
|
|
||||||
return f.pos
|
|
||||||
}
|
|
||||||
|
|
||||||
// SeekTo sets the current file pointer relative to the start of the file.
|
|
||||||
func (f *mmapFile) SeekTo(offset uint64) {
|
|
||||||
f.pos = offset
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read implements io.Reader.
|
|
||||||
func (f *mmapFile) Read(p []byte) (int, error) {
|
|
||||||
if f.data == nil {
|
|
||||||
return 0, errMmapClosed
|
|
||||||
}
|
|
||||||
if f.pos >= f.Size() {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
n := copy(p, f.data[f.pos:])
|
|
||||||
f.pos += uint64(n)
|
|
||||||
if n < len(p) {
|
|
||||||
return n, io.EOF
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadByte implements io.ByteReader.
|
|
||||||
func (f *mmapFile) ReadByte() (byte, error) {
|
|
||||||
if f.data == nil {
|
|
||||||
return 0, errMmapClosed
|
|
||||||
}
|
|
||||||
if f.pos >= f.Size() {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
b := f.data[f.pos]
|
|
||||||
f.pos++
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadSlice returns a slice of size n that points directly at the
|
|
||||||
// underlying mapped file. There is no copying. Fails if it cannot
|
|
||||||
// read at least n bytes.
|
|
||||||
func (f *mmapFile) ReadSlice(n uint64) ([]byte, error) {
|
|
||||||
if f.data == nil {
|
|
||||||
return nil, errMmapClosed
|
|
||||||
}
|
|
||||||
if f.pos+n >= f.Size() {
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
first := f.pos
|
|
||||||
f.pos += n
|
|
||||||
return f.data[first:f.pos:f.pos], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadSliceAt is like ReadSlice, but reads from a specific offset.
|
|
||||||
// The file pointer is not used or advanced.
|
|
||||||
func (f *mmapFile) ReadSliceAt(offset, n uint64) ([]byte, error) {
|
|
||||||
if f.data == nil {
|
|
||||||
return nil, errMmapClosed
|
|
||||||
}
|
|
||||||
if f.Size() < offset {
|
|
||||||
return nil, fmt.Errorf("mmap: out-of-bounds ReadSliceAt offset %d, size is %d", offset, f.Size())
|
|
||||||
}
|
|
||||||
if offset+n >= f.Size() {
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
end := offset + n
|
|
||||||
return f.data[offset:end:end], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the file.
|
|
||||||
func (f *mmapFile) Close() error {
|
|
||||||
if f.data == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := syscall.Munmap(f.data)
|
|
||||||
f.data = nil
|
|
||||||
f.pos = 0
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build !darwin,!linux
|
|
||||||
|
|
||||||
package core
|
|
||||||
|
|
||||||
// TODO(matloob): perhaps use the more portable golang.org/x/exp/mmap
|
|
||||||
// instead of the mmap code in mmapfile.go.
|
|
||||||
|
|
||||||
type mmapFile struct{}
|
|
||||||
|
|
||||||
func (m *mmapFile) Close() error { return nil }
|
|
@ -1,308 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package core provides functions for reading core dumps
|
|
||||||
// and examining their contained heaps.
|
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RawDump provides raw access to the heap records in a core file.
|
|
||||||
// The raw records in this file are described by other structs named Raw{*}.
|
|
||||||
// All []byte slices are direct references to the underlying mmap'd file.
|
|
||||||
// These references will become invalid as soon as the RawDump is closed.
|
|
||||||
type RawDump struct {
|
|
||||||
Params *RawParams
|
|
||||||
MemStats *runtime.MemStats
|
|
||||||
|
|
||||||
HeapObjects []RawSegment // heap objects sorted by Addr, low-to-high
|
|
||||||
GlobalSegments []RawSegment // data, bss, and noptrbss segments
|
|
||||||
|
|
||||||
OSThreads []*RawOSThread
|
|
||||||
Goroutines []*RawGoroutine
|
|
||||||
StackFrames []*RawStackFrame
|
|
||||||
OtherRoots []*RawOtherRoot
|
|
||||||
Finalizers []*RawFinalizer
|
|
||||||
Defers []*RawDefer
|
|
||||||
Panics []*RawPanic
|
|
||||||
|
|
||||||
TypeFromItab map[uint64]uint64 // map from itab address to the type address that itab represents
|
|
||||||
TypeFromAddr map[uint64]*RawType // map from RawType.Addr to RawType
|
|
||||||
|
|
||||||
MemProfMap map[uint64]*RawMemProfEntry
|
|
||||||
AllocSamples []*RawAllocSample
|
|
||||||
|
|
||||||
fmap *mmapFile
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawParams holds metadata about the program that generated the dump.
|
|
||||||
type RawParams struct {
|
|
||||||
// Info about the memory space
|
|
||||||
|
|
||||||
ByteOrder binary.ByteOrder // byte order of all memory in this dump
|
|
||||||
PtrSize uint64 // in bytes
|
|
||||||
HeapStart uint64 // heap start address
|
|
||||||
HeapEnd uint64 // heap end address (this is the last byte in the heap + 1)
|
|
||||||
|
|
||||||
// Info about the program that generated this heapdump
|
|
||||||
|
|
||||||
GoArch string // GOARCH of the runtime library that generated this dump
|
|
||||||
GoExperiment string // GOEXPERIMENT of the toolchain that build the runtime library
|
|
||||||
NCPU uint64 // number of physical cpus available to the program
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawSegment represents a segment of memory.
|
|
||||||
type RawSegment struct {
|
|
||||||
Addr uint64 // base address of the segment
|
|
||||||
Data []byte // data for this segment
|
|
||||||
PtrFields RawPtrFields // offsets of ptr fields within this segment
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawPtrFields represents a pointer field.
|
|
||||||
type RawPtrFields struct {
|
|
||||||
encoded []byte // list of uvarint-encoded offsets, or nil if none
|
|
||||||
startOff, endOff uint64 // decoded offsets are translated and clipped to [startOff,endOff)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawOSThread represents an OS thread.
|
|
||||||
type RawOSThread struct {
|
|
||||||
MAddr uint64 // address of the OS thread descriptor (M)
|
|
||||||
GoID uint64 // go's internal ID for the thread
|
|
||||||
ProcID uint64 // kernel's ID for the thread
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawGoroutine represents a goroutine structure.
|
|
||||||
type RawGoroutine struct {
|
|
||||||
GAddr uint64 // address of the goroutine descriptor
|
|
||||||
SP uint64 // current stack pointer (lowest address in the currently running frame)
|
|
||||||
GoID uint64 // goroutine ID
|
|
||||||
GoPC uint64 // PC of the go statement that created this goroutine
|
|
||||||
Status uint64
|
|
||||||
IsSystem bool // true if started by the system
|
|
||||||
IsBackground bool // always false in go1.7
|
|
||||||
WaitSince uint64 // time the goroutine started waiting, in nanoseconds since the Unix epoch
|
|
||||||
WaitReason string
|
|
||||||
CtxtAddr uint64 // address of the scheduling ctxt
|
|
||||||
MAddr uint64 // address of the OS thread descriptor (M)
|
|
||||||
TopDeferAddr uint64 // address of the top defer record
|
|
||||||
TopPanicAddr uint64 // address of the top panic record
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawStackFrame represents a stack frame.
|
|
||||||
type RawStackFrame struct {
|
|
||||||
Name string
|
|
||||||
Depth uint64 // 0 = bottom of stack (currently running frame)
|
|
||||||
CalleeSP uint64 // stack pointer of the child frame (or 0 for the bottom-most frame)
|
|
||||||
EntryPC uint64 // entry PC for the function
|
|
||||||
PC uint64 // current PC being executed
|
|
||||||
NextPC uint64 // for callers, where the function resumes (if anywhere) after the callee is done
|
|
||||||
Segment RawSegment // local vars (Segment.Addr is the stack pointer, i.e., lowest address in the frame)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawOtherRoot represents the other roots not in RawDump's other fields.
|
|
||||||
type RawOtherRoot struct {
|
|
||||||
Description string
|
|
||||||
Addr uint64 // address pointed to by this root
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawFinalizer represents a finalizer.
|
|
||||||
type RawFinalizer struct {
|
|
||||||
IsQueued bool // if true, the object is unreachable and the finalizer is ready to run
|
|
||||||
ObjAddr uint64 // address of the object to finalize
|
|
||||||
ObjTypeAddr uint64 // address of the descriptor for typeof(obj)
|
|
||||||
FnAddr uint64 // function to be run (a FuncVal*)
|
|
||||||
FnArgTypeAddr uint64 // address of the descriptor for the type of the function argument
|
|
||||||
FnPC uint64 // PC of finalizer entry point
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawDefer represents a defer.
|
|
||||||
type RawDefer struct {
|
|
||||||
Addr uint64 // address of the defer record
|
|
||||||
GAddr uint64 // address of the containing goroutine's descriptor
|
|
||||||
ArgP uint64 // stack pointer giving the args for defer (TODO: is this right?)
|
|
||||||
PC uint64 // PC of the defer instruction
|
|
||||||
FnAddr uint64 // function to be run (a FuncVal*)
|
|
||||||
FnPC uint64 // PC of the defered function's entry point
|
|
||||||
LinkAddr uint64 // address of the next defer record in this chain
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawPanic represents a panic.
|
|
||||||
type RawPanic struct {
|
|
||||||
Addr uint64 // address of the panic record
|
|
||||||
GAddr uint64 // address of the containing goroutine's descriptor
|
|
||||||
ArgTypeAddr uint64 // type of the panic arg
|
|
||||||
ArgAddr uint64 // address of the panic arg
|
|
||||||
DeferAddr uint64 // address of the defer record that is currently running
|
|
||||||
LinkAddr uint64 // address of the next panic record in this chain
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawType repesents the Go runtime's representation of a type.
|
|
||||||
type RawType struct {
|
|
||||||
Addr uint64 // address of the type descriptor
|
|
||||||
Size uint64 // in bytes
|
|
||||||
Name string // not necessarily unique
|
|
||||||
// If true, this type is equivalent to a single pointer, so ifaces can store
|
|
||||||
// this type directly in the data field (without indirection).
|
|
||||||
DirectIFace bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawMemProfEntry represents a memory profiler entry.
|
|
||||||
type RawMemProfEntry struct {
|
|
||||||
Size uint64 // size of the allocated object
|
|
||||||
NumAllocs uint64 // number of allocations
|
|
||||||
NumFrees uint64 // number of frees
|
|
||||||
Stacks []RawMemProfFrame // call stacks
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawMemProfFrame represents a memory profiler frame.
|
|
||||||
type RawMemProfFrame struct {
|
|
||||||
Func []byte // string left as []byte reference to save memory
|
|
||||||
File []byte // string left as []byte reference to save memory
|
|
||||||
Line uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawAllocSample represents a memory profiler allocation sample.
|
|
||||||
type RawAllocSample struct {
|
|
||||||
Addr uint64 // address of object
|
|
||||||
Prof *RawMemProfEntry // record of allocation site
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the file.
|
|
||||||
func (r *RawDump) Close() error {
|
|
||||||
return r.fmap.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindSegment returns the segment that contains the given address, or
|
|
||||||
// nil of no segment contains the address.
|
|
||||||
func (r *RawDump) FindSegment(addr uint64) *RawSegment {
|
|
||||||
// Binary search for an upper-bound heap object, then check
|
|
||||||
// if the previous object contains addr.
|
|
||||||
k := sort.Search(len(r.HeapObjects), func(k int) bool {
|
|
||||||
return addr < r.HeapObjects[k].Addr
|
|
||||||
})
|
|
||||||
k--
|
|
||||||
if k >= 0 && r.HeapObjects[k].Contains(addr) {
|
|
||||||
return &r.HeapObjects[k]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check all global segments.
|
|
||||||
for k := range r.GlobalSegments {
|
|
||||||
if r.GlobalSegments[k].Contains(addr) {
|
|
||||||
return &r.GlobalSegments[k]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NB: Stack-local vars are technically allocated in the heap, since stack frames are
|
|
||||||
// allocated in the heap space, however, stack frames don't show up in r.HeapObjects.
|
|
||||||
for _, f := range r.StackFrames {
|
|
||||||
if f.Segment.Contains(addr) {
|
|
||||||
return &f.Segment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contains returns true if the segment contains the given address.
|
|
||||||
func (r RawSegment) Contains(addr uint64) bool {
|
|
||||||
return r.Addr <= addr && addr < r.Addr+r.Size()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainsRange returns true if the segment contains the range [addr, addr+size).
|
|
||||||
func (r RawSegment) ContainsRange(addr, size uint64) bool {
|
|
||||||
if !r.Contains(addr) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if size > 0 && !r.Contains(addr+size-1) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns the size of the segment in bytes.
|
|
||||||
func (r RawSegment) Size() uint64 {
|
|
||||||
return uint64(len(r.Data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slice takes a slice of the given segment. Panics if [offset,offset+size)
|
|
||||||
// is out-of-bounds. The resulting RawSegment.PtrOffsets will clipped and
|
|
||||||
// translated into the new segment.
|
|
||||||
func (r RawSegment) Slice(offset, size uint64) *RawSegment {
|
|
||||||
if offset+size > uint64(len(r.Data)) {
|
|
||||||
panic(fmt.Errorf("slice(%d,%d) out-of-bounds of segment @%x sz=%d", offset, size, r.Addr, len(r.Data)))
|
|
||||||
}
|
|
||||||
return &RawSegment{
|
|
||||||
Addr: r.Addr + offset,
|
|
||||||
Data: r.Data[offset : offset+size : offset+size],
|
|
||||||
PtrFields: RawPtrFields{
|
|
||||||
encoded: r.PtrFields.encoded,
|
|
||||||
startOff: r.PtrFields.startOff + offset,
|
|
||||||
endOff: r.PtrFields.startOff + offset + size,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Offsets decodes the list of ptr field offsets.
|
|
||||||
func (r RawPtrFields) Offsets() []uint64 {
|
|
||||||
if r.encoded == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NB: This should never fail since we already decoded the varints once
|
|
||||||
// when parsing the file originally. Hence we panic on failure.
|
|
||||||
reader := bytes.NewReader(r.encoded)
|
|
||||||
readUint64 := func() uint64 {
|
|
||||||
x, err := binary.ReadUvarint(reader)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Errorf("unexpected failure decoding uvarint: %v", err))
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
var out []uint64
|
|
||||||
for {
|
|
||||||
k := readUint64()
|
|
||||||
switch k {
|
|
||||||
case 0: // end
|
|
||||||
return out
|
|
||||||
case 1: // ptr
|
|
||||||
x := readUint64()
|
|
||||||
if r.startOff <= x && x < r.endOff {
|
|
||||||
out = append(out, x-r.startOff)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("unexpected FieldKind %d", k))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadPtr decodes a ptr from the given byte slice.
|
|
||||||
func (r *RawParams) ReadPtr(b []byte) uint64 {
|
|
||||||
switch r.PtrSize {
|
|
||||||
case 4:
|
|
||||||
return uint64(r.ByteOrder.Uint32(b))
|
|
||||||
case 8:
|
|
||||||
return r.ByteOrder.Uint64(b)
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("unsupported PtrSize=%d", r.PtrSize))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WritePtr encodes a ptr into the given byte slice.
|
|
||||||
func (r *RawParams) WritePtr(b []byte, addr uint64) {
|
|
||||||
switch r.PtrSize {
|
|
||||||
case 4:
|
|
||||||
r.ByteOrder.PutUint32(b, uint32(addr))
|
|
||||||
case 8:
|
|
||||||
r.ByteOrder.PutUint64(b, addr)
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("unsupported PtrSize=%d", r.PtrSize))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// heapview is a tool for viewing Go heap dumps.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"go/build"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
|
||||||
|
|
||||||
var host = flag.String("host", "", "host addr to listen on")
|
|
||||||
var port = flag.Int("port", 8080, "service port")
|
|
||||||
|
|
||||||
var index = `<!DOCTYPE html>
|
|
||||||
<script src="js/customelements.js"></script>
|
|
||||||
<script src="js/typescript.js"></script>
|
|
||||||
<script src="js/moduleloader.js"></script>
|
|
||||||
<script>
|
|
||||||
System.transpiler = 'typescript';
|
|
||||||
System.typescriptOptions = {target: ts.ScriptTarget.ES2015};
|
|
||||||
System.locate = (load) => load.name + '.ts';
|
|
||||||
</script>
|
|
||||||
<script type="module">
|
|
||||||
import {main} from './client/main';
|
|
||||||
main();
|
|
||||||
</script>
|
|
||||||
`
|
|
||||||
|
|
||||||
func toolsDir() string {
|
|
||||||
p, err := build.Import("golang.org/x/tools", "", build.FindOnly)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("error: can't find client files:", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
return p.Dir
|
|
||||||
}
|
|
||||||
|
|
||||||
var parseFlags = func() {
|
|
||||||
flag.Parse()
|
|
||||||
}
|
|
||||||
|
|
||||||
var addHandlers = func() {
|
|
||||||
toolsDir := toolsDir()
|
|
||||||
|
|
||||||
// Directly serve typescript code in client directory for development.
|
|
||||||
http.Handle("/client/", http.StripPrefix("/client",
|
|
||||||
http.FileServer(http.Dir(filepath.Join(toolsDir, "cmd/heapview/client")))))
|
|
||||||
|
|
||||||
// Serve typescript.js and moduleloader.js for development.
|
|
||||||
http.HandleFunc("/js/typescript.js", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
http.ServeFile(w, r, filepath.Join(toolsDir, "third_party/typescript/typescript.js"))
|
|
||||||
})
|
|
||||||
http.HandleFunc("/js/moduleloader.js", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
http.ServeFile(w, r, filepath.Join(toolsDir, "third_party/moduleloader/moduleloader.js"))
|
|
||||||
})
|
|
||||||
http.HandleFunc("/js/customelements.js", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
http.ServeFile(w, r, filepath.Join(toolsDir, "third_party/webcomponents/customelements.js"))
|
|
||||||
})
|
|
||||||
|
|
||||||
// Serve index.html using html string above.
|
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "text/html")
|
|
||||||
io.WriteString(w, index)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var listenAndServe = func() error {
|
|
||||||
return http.ListenAndServe(fmt.Sprintf("%s:%d", *host, *port), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
parseFlags()
|
|
||||||
addHandlers()
|
|
||||||
log.Fatal(listenAndServe())
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user