def _timeline_from_nodestats(nodestats):
  """Return sorted memory allocation records from list of nodestats
  [NodeExecStats, NodeExecStats...], it's the
  run_metadata.step_stats.dev_stats[0].step_stats object.
  Timeline looks like this:
 timestamp         nodename  mem delta, allocator name
 [1509481813012895, 'concat', 1000496, 'cpu'],
 [1509481813012961, 'a04', -1000000, 'cpu'],
 [1509481813012968, 'TanhGrad', 0, 'cpu'], 
  0 memory allocation is reported for nodes without allocation_records
  """
  
  lines = []
  if not nodestats:
    return []
  for node in nodestats:
    for mem in node.memory:  # can have both cpu and gpu allocator for op
      try:
        records = mem.allocation_records
      except:
        records = []
      allocator = mem.allocator_name
      if len(records)>0:
        for record in records:
          line = [record.alloc_micros, node.node_name, record.alloc_bytes,
                  allocator]
          lines.append(line)
      else:
        output_bytes = -1
        try:
          output_bytes = node.output[0].tensor_description.allocation_description.requested_bytes
        except:
          pass
        line = [node.all_start_micros, node.node_name, 0, "unknown"]
        lines.append(line)
  def first_key(x): return x[0]
  return sorted(lines, key=first_key)