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

Adding heatmap and graph_type #134

Merged
merged 25 commits into from
Jun 12, 2018
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions apps/xprof_gui/priv/src/actions/CollectingActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ export const getMonitoredFunctions = () => async (dispatch, getState) => {
if (error) {
console.log('ERROR: ', error);
} else if (!isEqual(mfas, json)) {
const newMfas = json.filter(mfa => !mfas.map(f => f[3]).includes(mfa[3]));
const newMfas = json
.filter(mfa => !mfas.map(f => f.query)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpicking: you could perform and collect mfas.map(f => f.query) before filtering to do it only once instead of doing it for each mfa from incomming json

Copy link
Contributor Author

@jlampar jlampar May 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now it is:

const queries = mfas.map(f => f.query);
const newMfas = json
  .filter(mfa => !queries
    .includes(mfa.query));

(for the sake of clarity, given in old, already changed naming convention)

.includes(mfa.query));
const newControls = newMfas.reduce(
(control, mfa) => ({
...control,
[mfa[3]]: {
[mfa.query]: {
threshold: undefined,
limit: undefined,
collecting: false,
Expand Down
20 changes: 10 additions & 10 deletions apps/xprof_gui/priv/src/actions/ExploringActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ export const calleeClick = callee => (dispatch) => {
dispatch(startMonitoringFunction(callee));
};

export const getCallees = mfa => async (dispatch) => {
const name = mfa[3];
export const getCallees = m => async (dispatch) => {
const name = m.query;

const { json, error } = await XProf.getFunctionsCallees(
mfa[0],
mfa[1],
mfa[2],
m.mfa[0],
m.mfa[1],
m.mfa[2],
);

if (error) console.log('ERROR');
Expand All @@ -39,13 +39,13 @@ export const getCallees = mfa => async (dispatch) => {
export const getCalleesForFunctions = mfas => async (dispatch) => {
const callees = {};

await Promise.all(mfas.map(async (mfa) => {
const fun = mfa[3];
await Promise.all(mfas.map(async (m) => {
const fun = m.query;

const { json, error } = await XProf.getFunctionsCallees(
mfa[0],
mfa[1],
mfa[2],
m.mfa[0],
m.mfa[1],
m.mfa[2],
);

if (error) console.log('ERROR');
Expand Down
12 changes: 8 additions & 4 deletions apps/xprof_gui/priv/src/actions/MonitoringActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ const stopMonitoringFunctionError = mfas => ({
mfas,
});

export const stopMonitoringFunction = mfa => async (dispatch, getState) => {
export const stopMonitoringFunction = m => async (dispatch, getState) => {
const state = getState();
const mfas = getMfas(state);
const mfasReduced = mfas.filter(m => m[3] !== mfa[3]);
const mfasReduced = mfas.filter(f => f.query !== m.query);

dispatch(stopMonitoringFunctionRequest(mfasReduced));

const { error } = await XProf.stopMonitoringFunction(mfa[0], mfa[1], mfa[2]);
const { error } = await XProf.stopMonitoringFunction(
m.mfa[0],
m.mfa[1],
m.mfa[2],
);
if (error) {
console.log('ERROR: ', error);
dispatch(stopMonitoringFunctionError(mfas));
Expand All @@ -32,7 +36,7 @@ export const startMonitoringFunction = functionName => async (
) => {
const state = getState();
const mfas = getMfas(state);
const isMonitored = mfas.filter(mfa => mfa[3] === functionName).length;
const isMonitored = mfas.filter(mfa => mfa.query === functionName).length;

if (!isMonitored) {
const { error } = await XProf.startMonitoringFunction(functionName);
Expand Down
10 changes: 5 additions & 5 deletions apps/xprof_gui/priv/src/actions/TracingActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const updateLastCallsForFunction = (functionName, sortedCalls) => ({

export const toggleExpandItem = (mfa, item) => (dispatch, getState) => {
const state = getState();
const functionName = mfa[3];
const functionName = mfa.query;
const lastCallsForFunction = getLastCallsForFunction(state, functionName);

const updatedItems = lastCallsForFunction.sort.items.map((call) => {
Expand All @@ -40,15 +40,15 @@ export const toggleExpandItem = (mfa, item) => (dispatch, getState) => {

export const toggleCallsTracing = mfa => async (dispatch, getState) => {
const state = getState();
const functionName = mfa[3];
const functionName = mfa.query;
const control = getFunctionControl(state, functionName);
const nextControl = await determineNextControlSwitch(control, mfa);
dispatch(setCallsControl({ [functionName]: nextControl }));
};

export const handleThresholdChange = (mfa, value) => (dispatch, getState) => {
const state = getState();
const functionName = mfa[3];
const functionName = mfa.query;
const { limit, collecting } = getFunctionControl(state, functionName);
const nextControl = {
threshold: value,
Expand All @@ -60,7 +60,7 @@ export const handleThresholdChange = (mfa, value) => (dispatch, getState) => {

export const handleLimitChange = (mfa, value) => (dispatch, getState) => {
const state = getState();
const functionName = mfa[3];
const functionName = mfa.query;
const { threshold, collecting } = getFunctionControl(state, functionName);
const nextControl = {
threshold,
Expand All @@ -72,7 +72,7 @@ export const handleLimitChange = (mfa, value) => (dispatch, getState) => {

export const sortCallsBy = (mfa, column) => (dispatch, getState) => {
const state = getState();
const functionName = mfa[3];
const functionName = mfa.query;
const lastCallsForFunction = getLastCallsForFunction(state, functionName);
const order =
lastCallsForFunction.sort.column === column &&
Expand Down
14 changes: 9 additions & 5 deletions apps/xprof_gui/priv/src/components/functions/Functions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { FUNCTIONS_INTERVAL } from '../../constants';

const propTypes = {
getMonitoredFunctions: PropTypes.func.isRequired,
mfas: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.any)).isRequired,
mfas: PropTypes.arrayOf(PropTypes.shape({
graph_type: PropTypes.string,
mfa: PropTypes.arrayOf(PropTypes.any),
query: PropTypes.string,
})).isRequired,
};

class Functions extends React.Component {
Expand All @@ -27,10 +31,10 @@ class Functions extends React.Component {
const { mfas } = this.props;
return (
<div>
{mfas.map((mfa, index) => (
<div key={mfa[3]}>
<MonitoringContainer mfa={mfa} />
<TracingContainer mfa={mfa} />
{mfas.map((m, index) => (
<div key={m.query}>
<MonitoringContainer mfa={m} />
<TracingContainer mfa={m} />
{index < mfas.length - 1 ? (
<hr className="function-separator" />
) : null}
Expand Down
56 changes: 45 additions & 11 deletions apps/xprof_gui/priv/src/components/monitoring/Graph/Graph.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,55 @@ import React from 'react';
import C3Chart from 'react-c3js';
import 'c3/c3.css';
import { AXIS, DATA, GRID, POINT, TRANSITION } from '../../../constants';
import Grid from './Grid/Grid';

function composeID(q) {
const modFuncArity = q.split(':');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you shouldn't assume anything about the m.query it can have different format depending on Erlang or Elixir mode (eg Mod.fun/arity in case of Elixir) and it can have more advanced formats than just mod-fun-arity (eg #funcount mfa = mod:fun(1, _) )
I would be better to use m.mfa to compose the ID

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there used to be a utility function chartId which we stopped using, but it can be reintroduced for safe IDs https://github.com/Appliscale/xprof/blob/master/priv/app/utils.js#L2

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll use the chartId(MFA) function. If it worked before, it will work fine. Nevertheless I'll reintegrate it and test.

const mod = modFuncArity[0];
const func = modFuncArity[1].split('/')[0];
const arity = modFuncArity[1].split('/')[1];
return `${mod}${func}${arity}`;
}

const propTypes = {
dps: PropTypes.arrayOf(PropTypes.object).isRequired,
type: PropTypes.string.isRequired,
query: PropTypes.string.isRequired,
Copy link
Collaborator

@gomoripeti gomoripeti May 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this can be the whole m json object (including mfa and query fields) to address above issue with composing the ID
(we need to find a good name for the json object, I dont know if function is good enough, in generic sense this is a monitor or monitored-object, which can be an m:f/a but also can be garbage_collection or memory in the future)

Copy link
Contributor Author

@jlampar jlampar May 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are composing the ID inside GraphPanel, in the end we are only passing the ID to the Graph and the panel has access to the whole m object. The panel needs the object as it decomposes it into the grid props, whereas the grid just receives stuff from the panel.
In case of renaming the JSON object I think this is a good idea, as for now the object named mfa has also a mfa property which is a little bit confusing (that's why I renamed it somewhere to m in loops or arguments).
I propose we:

  • rename the mfas to monitoredCollection,
  • rename the mfa to monitored.

What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, so query is only there so that the Graph can show it as a title. +1 for renamings

};

const Graph = ({ dps, type, query }) => {
const queryID = composeID(query);
const wrapperID = `graphWrapper-${queryID}`;
const wrapper = document.getElementById(wrapperID);
if (type === 'grid') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can create constant for types and put it somewhere in src/constants

export const GRAPH_TYPES = {
  GRID: 'grid',
  LINE: 'line',
}

return (
<div
id={wrapperID}
style={{
textAlign: 'center',
justifyContent: 'center',
}}
>
{wrapper && <Grid
data={{ ...DATA, json: dps }}
outerWidth={wrapper.clientWidth}
graphID={queryID}
/>}
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we introduced new type of graph we can add new layer of components here

  switch (type) {
    case GRAPH_TYPES.GRID:
      return (<GridGraph />);
    case GRAPH_TYPES.LINE:
      return (<LineGraph />);
    default:
      return
  }
     

);
}
return (
<div>
<C3Chart
data={{ ...DATA, json: dps }}
point={POINT}
grid={GRID}
axis={AXIS}
transition={TRANSITION}
/>
</div>
);
};
const Graph = ({ dps }) => (
<div>
<C3Chart
data={{ ...DATA, json: dps }}
point={POINT}
grid={GRID}
axis={AXIS}
transition={TRANSITION}
/>
</div>
);
Graph.propTypes = propTypes;

export default Graph;
Loading