Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plotting memory usage of allocations from given backtrace #46

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
89 changes: 69 additions & 20 deletions server-core/src/lib.rs
Expand Up @@ -487,6 +487,7 @@ fn handler_timeline( req: HttpRequest ) -> Result< HttpResponse > {
let mut allocations = Vec::with_capacity( maximum_len );
let mut deallocations = Vec::with_capacity( maximum_len );
let mut x = (-1_i32) as u64;
let filter: protocol::AllocFilter = query( &req )?;

for op in data.operations() {
let timestamp = match op {
Expand All @@ -495,6 +496,34 @@ fn handler_timeline( req: HttpRequest ) -> Result< HttpResponse > {
Operation::Reallocation { ref new_allocation, .. } => new_allocation.timestamp
};

let mut filter_out_backtrace = false;
let mut filter_in_reallocated_backtrace = false;
match filter.backtraces {
Some(v) => {
match op {
Operation::Allocation { ref allocation, .. } => {
if allocation.backtrace != BacktraceId::new( v as _ ) {
filter_out_backtrace = true;
}
},
Operation::Deallocation { ref allocation, .. } => {
if allocation.backtrace != BacktraceId::new( v as _ ) {
filter_out_backtrace = true;
}
},
Operation::Reallocation { ref new_allocation, ref old_allocation, .. } => {
if old_allocation.backtrace != BacktraceId::new( v as _ ) {
filter_out_backtrace = true;
}
if new_allocation.backtrace == BacktraceId::new( v as _ ) {
filter_in_reallocated_backtrace = true;
}
}
};
},
None => {},
}

let timestamp = timestamp.as_secs();
let timestamp_changed = timestamp != x;

Expand Down Expand Up @@ -556,29 +585,49 @@ fn handler_timeline( req: HttpRequest ) -> Result< HttpResponse > {
let leaked_size = leaked_size.last_mut().unwrap();
let leaked_count = leaked_count.last_mut().unwrap();

let (size_delta_v, count_delta_v) = match op {
Operation::Allocation { allocation, .. } => {
*allocations += 1;
if allocation.deallocation.is_none() {
*leaked_size += allocation.size;
*leaked_count += 1;
let (size_delta_v, count_delta_v) = if filter_out_backtrace {
match op {
Operation::Reallocation { new_allocation, old_allocation, .. } => {
if filter_in_reallocated_backtrace {
if new_allocation.deallocation.is_none() {
*leaked_size += new_allocation.size;
*leaked_count += 1;
}
(new_allocation.size as i64, 1)
} else {
(0, 0)
}
},
_ => {
// This operation is from other backtrace, skip it
(0, 0)
}
}
} else {
match op {
Operation::Allocation { allocation, .. } => {
*allocations += 1;
if allocation.deallocation.is_none() {
*leaked_size += allocation.size;
*leaked_count += 1;
}

(allocation.size as i64, 1)
},
Operation::Deallocation { allocation, .. } => {
*deallocations += 1;
(allocation.size as i64 * -1, -1)
},
Operation::Reallocation { new_allocation, old_allocation, .. } => {
*allocations += 1;
*deallocations += 1;
if new_allocation.deallocation.is_none() {
*leaked_size += new_allocation.size;
*leaked_count += 1;
}
(allocation.size as i64, 1)
},
Operation::Deallocation { allocation, .. } => {
*deallocations += 1;
(allocation.size as i64 * -1, -1)
},
Operation::Reallocation { new_allocation, old_allocation, .. } => {
*allocations += 1;
*deallocations += 1;
if new_allocation.deallocation.is_none() {
*leaked_size += new_allocation.size;
*leaked_count += 1;
}

(new_allocation.size as i64 - old_allocation.size as i64, 0)
(new_allocation.size as i64 - old_allocation.size as i64, 0)
}
}
};

Expand Down
61 changes: 54 additions & 7 deletions webui/src/PageDataAllocations.js
@@ -1,6 +1,7 @@
import _ from "lodash";
import React from "react";
import ReactTable from "react-table";
import Graph from "./Graph.js";
import { FormGroup, Label, Input, Button, ButtonGroup, Modal, ModalFooter, ModalBody, ModalHeader, Badge } from "reactstrap";
import { Link } from "react-router-dom";
import { ContextMenu, MenuItem, ContextMenuTrigger } from "react-contextmenu";
Expand All @@ -9,6 +10,24 @@ import Feather from "./Feather.js";
import Tabbed from "./Tabbed.js";
import { fmt_size, fmt_date_unix, fmt_date_timeval, fmt_hex16, fmt_uptime, fmt_uptime_timeval, update_query, create_query, extract_query, format_frame } from "./utils.js";

export class BacktraceGraph extends Graph {

componentDidMount() {
super.componentDidMount();
fetch( (this.props.sourceUrl || "") + "/data/" + this.props.id + "/timeline?backtraces=" + this.props.backtrace_id )
.then( rsp => rsp.json() )
.then( json => {
//this.setState( {data: json} )
this.props.data = json;
//this.cache = None;
//this.forceRefresh();
this.forceRefresh();
this.forceUpdate();
});
}

}

const PERCENTAGE_REGEX = /^(\d+)%$/;
const DATE_REGEX = /^(\d{4})-(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})$/;
const SIZE_REGEX = /^(\d+)(k|m|g|t)?$/;
Expand Down Expand Up @@ -1013,6 +1032,8 @@ export default class PageDataAllocations extends React.Component {
expanded[ i ] = true;
}

let placeholder = {"xs":[1594837555, 1594837555], "allocated_size":[1, 2]};

return (
<div className="PageDataAllocations">
<Control
Expand Down Expand Up @@ -1071,16 +1092,42 @@ export default class PageDataAllocations extends React.Component {
SubComponent={row => {
const cell = backtrace_cell( show_full_backtraces, row.original.backtrace );

const q = _.omit( extract_query( this.props.location.search ), "count", "skip", "group_allocations", "sort_by", "order" );
const params = extract_query( this.props.location.search );
const q = _.omit( params, "count", "skip", "group_allocations", "sort_by", "order" );
q.backtraces = row.original.backtrace_id;
const url = "/#" + this.props.location.pathname + "?" + create_query( q ).toString();

return <div className="backtrace-cell" onContextMenu={event => {
this.setState({
showOnlyAllocationsUrl: url
});
return this.menu_trigger.handleContextClick( event );
}}>{cell}</div>;
const group_allocations = params.group_allocations === "true" || params.group_allocations === "1";
if(group_allocations) {
return <div className="backtrace-cell" onContextMenu={event => {
this.setState({
showOnlyAllocationsUrl: url
});
return this.menu_trigger.handleContextClick( event );
}}>
<BacktraceGraph
key={String(this.props.id)+"_"+String(row.original.backtrace_id)+String(Math.random())}
title="Memory usage"
data={placeholder}
y_accessor="allocated_size"
y_label=""
x0={this.state.x0}
x1={this.state.x1}
fill={true}
xUnit="unix_timestamp"
id={this.props.id}
sourceUrl={this.props.sourceUrl}
backtrace_id={row.original.backtrace_id}
/>
{cell}</div>;
} else {
return <div className="backtrace-cell" onContextMenu={event => {
this.setState({
showOnlyAllocationsUrl: url
});
return this.menu_trigger.handleContextClick( event );
}}>{cell}</div>;
}
}}
expanded={expanded}
/>
Expand Down